ソースを参照

1、我的收藏实现

2、商品详情实现收藏与取消收藏
tags/0.0.2^2
PH2 4年前
コミット
f0c8142df4
25個のファイルの変更1820行の追加78行の削除
  1. +0
    -22
      lib/pages/favorite_page/favorite_page.dart
  2. +0
    -17
      lib/pages/favorite_page/preview_page.dart
  3. +49
    -0
      lib/pages/favorites_page/bloc/favorites_bloc.dart
  4. +7
    -0
      lib/pages/favorites_page/bloc/favorites_event.dart
  5. +39
    -0
      lib/pages/favorites_page/bloc/favorites_repository.dart
  6. +23
    -0
      lib/pages/favorites_page/bloc/favorites_state.dart
  7. +264
    -0
      lib/pages/favorites_page/favorites_page.dart
  8. +278
    -0
      lib/pages/favorites_page/model/favorites_style_model.dart
  9. +44
    -0
      lib/pages/favorites_page/notifier/favorites_page_notifier.dart
  10. +6
    -2
      lib/register.dart
  11. +75
    -0
      lib/widgets/favorites/dialog/favorites_dialog_widget.dart
  12. +173
    -0
      lib/widgets/favorites/goods_list/bloc/favorites_goods_list_bloc.dart
  13. +39
    -0
      lib/widgets/favorites/goods_list/bloc/favorites_goods_list_event.dart
  14. +161
    -0
      lib/widgets/favorites/goods_list/bloc/favorites_goods_list_repository.dart
  15. +35
    -0
      lib/widgets/favorites/goods_list/bloc/favorites_goods_list_state.dart
  16. +325
    -0
      lib/widgets/favorites/goods_list/favorites_goods_list_widget.dart
  17. +37
    -0
      lib/widgets/favorites/goods_list/model/favorites_goods_list_model.dart
  18. +23
    -2
      lib/widgets/goods_details/footer/bloc/goods_details_footer_bloc.dart
  19. +15
    -0
      lib/widgets/goods_details/footer/bloc/goods_details_footer_event.dart
  20. +36
    -4
      lib/widgets/goods_details/footer/bloc/goods_details_footer_repository.dart
  21. +7
    -7
      lib/widgets/goods_details/footer/bloc/goods_details_footer_state.dart
  22. +10
    -3
      lib/widgets/goods_details/footer/goods_details_footer_widget.dart
  23. +153
    -19
      lib/widgets/goods_details/footer/model/goods_details_footer_model.dart
  24. +6
    -2
      lib/widgets/home/home_goods/home_goods_item_single.dart
  25. +15
    -0
      lib/widgets/home/home_goods/models/home_goods_model.dart

+ 0
- 22
lib/pages/favorite_page/favorite_page.dart ファイルの表示

@@ -1,22 +0,0 @@
import 'package:flutter/material.dart';

class FavoritePage extends StatefulWidget {
@override
_FavoritePageState createState() => _FavoritePageState();
}

class _FavoritePageState extends State<FavoritePage> {
GlobalKey _globalKey = GlobalKey();

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('收藏夹'),
),
body: Column(
children: <Widget>[],
),
);
}
}

+ 0
- 17
lib/pages/favorite_page/preview_page.dart ファイルの表示

@@ -1,17 +0,0 @@
import 'package:flutter/material.dart';

class PreviewPage extends StatelessWidget {
final Image image;

const PreviewPage({Key key, this.image}) : super(key: key);

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: image,
),
);
}
}

+ 49
- 0
lib/pages/favorites_page/bloc/favorites_bloc.dart ファイルの表示

@@ -0,0 +1,49 @@
import 'dart:async';

import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:meta/meta.dart';
import 'package:zhiying_base_widget/pages/favorites_page/bloc/favorites_repository.dart';
import 'package:zhiying_base_widget/pages/favorites_page/model/favorites_style_model.dart';
import 'package:zhiying_comm/util/empty_util.dart';

part 'favorites_event.dart';

part 'favorites_state.dart';

class FavoritesBloc extends Bloc<FavoritesEvent, FavoritesState> {
// FavoritesBloc() : super(FavoritesInitial());

FavoritesRepository repository;

FavoritesBloc(this.repository);

@override
// TODO: implement initialState
FavoritesState get initialState => FavoritesInitial();

@override
Stream<FavoritesState> mapEventToState(
FavoritesEvent event,
) async* {
/// 初始化
if (event is FavoritesInitEvent) {
yield* _mapInitEventToState(event);
}
}

/// 初始化
Stream<FavoritesState> _mapInitEventToState(FavoritesInitEvent event) async* {
var cache = await repository.fetchCachedStyle();
if (!EmptyUtil.isEmpty(cache)) {
yield FavoritesLoadedState(model: cache);
}
var result = await repository.fetchNetStyle();
if (!EmptyUtil.isEmpty(result)) {
yield FavoritesLoadedState(model: result);
} else {
yield FavoritesErrorState();
}
}
}

+ 7
- 0
lib/pages/favorites_page/bloc/favorites_event.dart ファイルの表示

@@ -0,0 +1,7 @@
part of 'favorites_bloc.dart';

@immutable
abstract class FavoritesEvent {}

/// 初始化数据
class FavoritesInitEvent extends FavoritesEvent {}

+ 39
- 0
lib/pages/favorites_page/bloc/favorites_repository.dart ファイルの表示

@@ -0,0 +1,39 @@
import 'dart:convert';

import 'package:zhiying_base_widget/pages/favorites_page/model/favorites_style_model.dart';
import 'package:zhiying_base_widget/widgets/empty/empty_widget.dart';
import 'package:zhiying_comm/zhiying_comm.dart';

class FavoritesRepository {
/// 请求网络样式
Future<FavoritesStyleModel> fetchNetStyle() async {
try {
var result = await NetUtil.post('/api/v1/mod/pub.flutter.my_fav', cache: true, method: NetMethod.GET);
if (NetUtil.isSuccess(result) && !EmptyUtil.isEmpty(result[GlobalConfig.HTTP_RESPONSE_KEY_DATA])) {
var modListData = result[GlobalConfig.HTTP_RESPONSE_KEY_DATA]['mod_list'][0]['data'];
if(!EmptyUtil.isEmpty(modListData)){
return FavoritesStyleModel.fromJson(jsonDecode(modListData));
}
}
} catch (e, s) {
Logger.log(e, s);
}
return null;
}

/// 获取缓存样式
Future<FavoritesStyleModel> fetchCachedStyle() async {
try {
var result = await NetUtil.getRequestCachedData('');
if(!EmptyUtil.isEmpty(result)){
var modListData = result['mod_list'][0]['data'];
if(!EmptyUtil.isEmpty(modListData)){
return FavoritesStyleModel.fromJson(jsonDecode(modListData));
}
}
} catch (e, s) {
Logger.log(e, s);
}
return null;
}
}

+ 23
- 0
lib/pages/favorites_page/bloc/favorites_state.dart ファイルの表示

@@ -0,0 +1,23 @@
part of 'favorites_bloc.dart';

@immutable
abstract class FavoritesState extends Equatable {
@override
List<Object> get props => [];
}

/// 初始化状态(骨架图)
class FavoritesInitial extends FavoritesState {}

/// 数据加载成功
class FavoritesLoadedState extends FavoritesState {
FavoritesStyleModel model;

FavoritesLoadedState({@required this.model});

@override
List<Object> get props => [this.model];
}

/// 未知错误
class FavoritesErrorState extends FavoritesState {}

+ 264
- 0
lib/pages/favorites_page/favorites_page.dart ファイルの表示

@@ -0,0 +1,264 @@
import 'package:flutter/material.dart';
import 'package:tab_indicator_styler/tab_indicator_styler.dart';
import 'package:zhiying_base_widget/pages/favorites_page/bloc/favorites_bloc.dart';
import 'package:zhiying_base_widget/pages/favorites_page/bloc/favorites_repository.dart';
import 'package:zhiying_base_widget/widgets/favorites/goods_list/favorites_goods_list_widget.dart';
import 'package:zhiying_comm/util/empty_util.dart';
import 'package:zhiying_comm/util/extension/color.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:provider/provider.dart';
import 'package:zhiying_comm/util/log/let_log.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:zhiying_comm/util/router_util.dart';

import 'model/favorites_style_model.dart';
import 'notifier/favorites_page_notifier.dart';

class FavoritesPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider.value(value: FavoritesPageNotifier()),
],
child: BlocProvider<FavoritesBloc>(
create: (_) => FavoritesBloc(FavoritesRepository())..add(FavoritesInitEvent()),
child: _FavoritesPageContainer(),
),
);
}
}

class _FavoritesPageContainer extends StatefulWidget {
@override
_FavoritesPageContainerState createState() => _FavoritesPageContainerState();
}

class _FavoritesPageContainerState extends State<_FavoritesPageContainer> with TickerProviderStateMixin {
TextEditingController _editingController;
FocusNode _focusNode;
TabController _tabController;
bool _isNotEditing = true;

/// 点击编辑
void _onClickEditing() {
RouterUtil.hideKeyboard(context);
setState(() {
_isNotEditing = !_isNotEditing;
});
Provider.of<FavoritesPageNotifier>(context, listen: false).editing();
}

/// 点击搜索
void _onClickSearch() {}

/// 执行搜索
void _submitSearch() {
String keyword = _editingController?.text?.toString()?.trim();
// if (!EmptyUtil.isEmpty(keyword)) {
_tabController.index = 0;
Provider.of<FavoritesPageNotifier>(context, listen: false).search(keyword);
// } else {
// Fluttertoast.showToast(msg: '请输入需要搜索的内容~');
// }
}

/// tabBar的初始化
void _tabControllerInit(FavoritesStyleModel model) {
if ((null == _tabController && !EmptyUtil.isEmpty(model?.tabList)) ||
(!EmptyUtil.isEmpty(model?.tabList) && null != _tabController && _tabController.length != model.tabList.length)) {
_tabController = TabController(length: model.tabList.length, vsync: this)
..addListener(() {
if (!_tabController.indexIsChanging) {
RouterUtil.hideKeyboard(context);
Logger.log('tab index = ${_tabController.index}');
Provider.of<FavoritesPageNotifier>(context, listen: false).changeSelectIndex(_tabController.index);
}
});
}
}

@override
void initState() {
_editingController = TextEditingController();
_focusNode = FocusNode();
super.initState();
}

@override
void dispose() {
_focusNode?.unfocus();
_focusNode?.dispose();
_editingController?.dispose();
super.dispose();
}

@override
Widget build(BuildContext context) {
return BlocConsumer<FavoritesBloc, FavoritesState>(
listener: (context, state) {},
buildWhen: (prev, current) {
return true;
},
builder: (context, state) {
if (state is FavoritesLoadedState) {
_tabControllerInit(state?.model);
return _buildMainWidget(state?.model);
}
return _buildSkeletonWidget();
},
);
}

/// mainWidget
Widget _buildMainWidget(FavoritesStyleModel model) {
return Scaffold(
resizeToAvoidBottomInset: false,
resizeToAvoidBottomPadding: false,
backgroundColor: HexColor.fromHex('#FFFFFF'),
appBar: _buildAppBarWidget(model),
body: Column(
children: <Widget>[
/// 搜索框
Padding(padding: const EdgeInsets.only(left: 12.5, right: 12.5, top: 4), child: _buildSearchWidget(model)),

/// tabBar
Padding(padding: const EdgeInsets.only(top: 4), child: _buildTabBarWidget(model)),

Expanded(
child: _buildTabBarViewWidget(model),
),
],
),
);
}

/// TabBar
Widget _buildTabBarWidget(FavoritesStyleModel model) {
return Container(
padding: const EdgeInsets.only(top: 12.5, bottom: 14.5),
width: double.infinity,
child: TabBar(
isScrollable: true,
labelStyle: TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
unselectedLabelStyle: TextStyle(fontWeight: FontWeight.w400, fontSize: 14),
labelColor: HexColor.fromHex(model?.tabNameSelectedColor ?? '#FF0100'),
unselectedLabelColor: HexColor.fromHex(model?.tabNameColor ?? '#333333'),
indicatorSize: TabBarIndicatorSize.label,
controller: _tabController,
indicatorWeight: 5,
indicator: MaterialIndicator(
topLeftRadius: 1,
topRightRadius: 1,
bottomLeftRadius: 1,
bottomRightRadius: 1,
height: 2,
color: HexColor.fromHex(model?.tabLineColor ?? '#FF0100'),
horizontalPadding: 5,
),
tabs: model.tabList.map((e) => Text(e?.name ?? '')).toList(),
),
);
}

/// tabBarView
Widget _buildTabBarViewWidget(FavoritesStyleModel model) {
int length = model?.tabList?.length ?? 0;
if (length > 0) {
int index = -1;
return TabBarView(
controller: _tabController,
children: model.tabList.map((e) {
++index;
return FavoritesGoodsListWidget(
model: model,
type: e?.type,
currentIndex: index,
);
}).toList(),
);
}
return Container(color: HexColor.fromHex('#F9F9F9'));
}

/// 搜索框
Widget _buildSearchWidget(FavoritesStyleModel model) {
return Container(
alignment: Alignment.center,
width: double.infinity,
height: 35,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(35 / 2),
color: HexColor.fromHex(model?.searchBarBgColor ?? '#F9F9F9'),
),
child: TextField(
controller: _editingController,
focusNode: _focusNode,
onSubmitted: (text) => _submitSearch(),
textInputAction: TextInputAction.search,
style: TextStyle(color: HexColor.fromHex('#333333'), fontSize: 14, textBaseline: TextBaseline.alphabetic),
textAlign: TextAlign.center,
decoration: InputDecoration(
fillColor: Colors.transparent,
filled: true,
isDense: true,
contentPadding: EdgeInsets.zero,
hintText: model?.searchBarText ?? '搜索商品',
hintStyle: TextStyle(color: HexColor.fromHex(model?.searchBarTextColor ?? '#999999'), fontSize: 14, textBaseline: TextBaseline.alphabetic),
border: InputBorder.none,
enabledBorder: InputBorder.none,
disabledBorder: InputBorder.none,
errorBorder: InputBorder.none,
focusedErrorBorder: InputBorder.none,
focusedBorder: InputBorder.none,
),
),
);
}

/// appbar
Widget _buildAppBarWidget(FavoritesStyleModel model) {
return AppBar(
backgroundColor: HexColor.fromHex(model?.appBarBgColor ?? '#FFFFFF'),
leading: IconButton(
icon: Icon(
Icons.arrow_back_ios,
size: 22,
color: HexColor.fromHex('#333333'),
),
onPressed: () => Navigator.maybePop(context),
),
title: Text(
model?.appBarName ?? '我的收藏',
style: TextStyle(color: HexColor.fromHex(model?.appBarNameColor ?? '#333333'), fontSize: 15, fontWeight: FontWeight.bold),
),
centerTitle: true,
elevation: 0,
actions: <Widget>[
GestureDetector(
onTap: () => _onClickEditing(),
behavior: HitTestBehavior.opaque,
child: Center(
child: Padding(
padding: const EdgeInsets.only(right: 12.5),
child: Text(
_isNotEditing ? model?.appBarRightText ?? '编辑' : model?.appBarRightEditingText ?? '完成',
style: TextStyle(
color: HexColor.fromHex(_isNotEditing ? model?.appBarRightTextColor ?? '#333333' : model?.appBarRightEditingColor ?? '#FF4242'),
fontSize: 14,
fontWeight: FontWeight.bold),
),
),
),
)
],
);
}

/// 骨架图
Widget _buildSkeletonWidget() {
return Scaffold(
body: Container(),
);
}
}

+ 278
- 0
lib/pages/favorites_page/model/favorites_style_model.dart ファイルの表示

@@ -0,0 +1,278 @@
import 'package:zhiying_base_widget/widgets/home/home_goods/models/home_goods_list_style_model.dart';

class FavoritesStyleModel {
String appBarName;
String appBarNameColor;
String appBarBgImg;
String appBarBgColor;
String appBarRightText;
String appBarRightTextColor;
String appBarRightEditingText;
String appBarRightEditingColor;
String searchBarIcon;
String searchBarText;
String searchBarTextColor;
String searchBarBgColor;
String tabLineColor;
String tabItemNotFoundImg;
List<TabList> tabList;
HomeGoodsListStyleModel listStyle;
String editLeftConfirmColor;
String editLeftNoConfirmColor;
Bottom bottom;
ConfirmDialog confirmDialog;

String tabNameColor;
String tabNameSelectedColor;
String editLeftConfirmIcon;
String editLeftNoConfirmIcon;

FavoritesStyleModel({
this.appBarName,
this.appBarNameColor,
this.appBarBgImg,
this.appBarBgColor,
this.appBarRightText,
this.appBarRightTextColor,
this.appBarRightEditingText,
this.appBarRightEditingColor,
this.searchBarIcon,
this.searchBarText,
this.searchBarTextColor,
this.searchBarBgColor,
this.tabLineColor,
this.tabItemNotFoundImg,
this.tabList,
this.listStyle,
this.editLeftConfirmColor,
this.editLeftNoConfirmColor,
this.bottom,
this.confirmDialog,
this.tabNameColor,
this.tabNameSelectedColor,
this.editLeftConfirmIcon,
this.editLeftNoConfirmIcon,
});

FavoritesStyleModel.fromJson(Map<String, dynamic> json) {
appBarName = json['app_bar_name'];
appBarNameColor = json['app_bar_name_color'];
appBarBgImg = json['app_bar_bg_img'];
appBarBgColor = json['app_bar_bg_color'];
appBarRightText = json['app_bar_right_text'];
appBarRightTextColor = json['app_bar_right_text_color'];
appBarRightEditingText = json['app_bar_right_editing_text'];
appBarRightEditingColor = json['app_bar_right_editing_color'];
searchBarIcon = json['search_bar_icon'];
searchBarText = json['search_bar_text'];
searchBarTextColor = json['search_bar_text_color'];
searchBarBgColor = json['search_bar_bg_color'];
tabLineColor = json['tab_line_color'];
tabItemNotFoundImg = json['tab_item_not_found_img'];
if (json['tab_list'] != null) {
tabList = new List<TabList>();
json['tab_list'].forEach((v) {
tabList.add(new TabList.fromJson(v));
});
}
listStyle = json['list_style'] != null ? new HomeGoodsListStyleModel.fromJson(json['list_style']) : null;
editLeftConfirmColor = json['edit_left_confirm_color'];
editLeftNoConfirmColor = json['edit_left_no_confirm_color'];
bottom = json['bottom'] != null ? new Bottom.fromJson(json['bottom']) : null;
confirmDialog = json['confirm_dialog'] != null ? new ConfirmDialog.fromJson(json['confirm_dialog']) : null;

tabNameColor = json['tab_name_color'];
tabNameSelectedColor = json['tab_name_selected_color'];
editLeftConfirmIcon = json['edit_left_confirm_icon'];
editLeftNoConfirmIcon = json['edit_left_no_confirm_icon'];

}

Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['app_bar_name'] = this.appBarName;
data['app_bar_name_color'] = this.appBarNameColor;
data['app_bar_bg_img'] = this.appBarBgImg;
data['app_bar_bg_color'] = this.appBarBgColor;
data['app_bar_right_text'] = this.appBarRightText;
data['app_bar_right_text_color'] = this.appBarRightTextColor;
data['app_bar_right_editing_text'] = this.appBarRightEditingText;
data['app_bar_right_editing_color'] = this.appBarRightEditingColor;
data['search_bar_icon'] = this.searchBarIcon;
data['search_bar_text'] = this.searchBarText;
data['search_bar_text_color'] = this.searchBarTextColor;
data['search_bar_bg_color'] = this.searchBarBgColor;
data['tab_line_color'] = this.tabLineColor;
data['tab_item_not_found_img'] = this.tabItemNotFoundImg;
if (this.tabList != null) {
data['tab_list'] = this.tabList.map((v) => v.toJson()).toList();
}
if (this.listStyle != null) {
data['list_style'] = this.listStyle.toJson();
}
data['edit_left_confirm_color'] = this.editLeftConfirmColor;
data['edit_left_no_confirm_color'] = this.editLeftNoConfirmColor;
if (this.bottom != null) {
data['bottom'] = this.bottom.toJson();
}
if (this.confirmDialog != null) {
data['confirm_dialog'] = this.confirmDialog.toJson();
}

data['tab_name_color'] = this.tabNameColor;
data['tab_name_selected_color'] = this.tabNameSelectedColor;
data['edit_left_confirm_icon'] = this.editLeftConfirmIcon;
data['edit_left_no_confirm_icon'] = this.editLeftNoConfirmIcon;

return data;
}
}

class TabList {
String type;
String name;
String nameColor;
String nameSelectedColor;

TabList({this.type, this.name, this.nameColor, this.nameSelectedColor});

TabList.fromJson(Map<String, dynamic> json) {
type = json['type'];
name = json['name'];
nameColor = json['name_color'];
nameSelectedColor = json['name_selected_color'];
}

Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['type'] = this.type;
data['name'] = this.name;
data['name_color'] = this.nameColor;
data['name_selected_color'] = this.nameSelectedColor;
return data;
}
}

class Left {
String couonText;
String couponFontColor;
String couponBgColor;
String couponBgImg;
String isImg;

Left({this.couonText, this.couponFontColor, this.couponBgColor, this.couponBgImg, this.isImg});

Left.fromJson(Map<String, dynamic> json) {
couonText = json['couon_text'];
couponFontColor = json['coupon_font_color'];
couponBgColor = json['coupon_bg_color'];
couponBgImg = json['coupon_bg_img'];
isImg = json['is_img'];
}

Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['couon_text'] = this.couonText;
data['coupon_font_color'] = this.couponFontColor;
data['coupon_bg_color'] = this.couponBgColor;
data['coupon_bg_img'] = this.couponBgImg;
data['is_img'] = this.isImg;
return data;
}
}

class Right {
String commissionText;
String commissionFontColor;
String commissionBgColor;
String commissionBgImg;
String isImg;

Right({this.commissionText, this.commissionFontColor, this.commissionBgColor, this.commissionBgImg, this.isImg});

Right.fromJson(Map<String, dynamic> json) {
commissionText = json['commission_text'];
commissionFontColor = json['commission_font_color'];
commissionBgColor = json['commission_bg_color'];
commissionBgImg = json['commission_bg_img'];
isImg = json['is_img'];
}

Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['commission_text'] = this.commissionText;
data['commission_font_color'] = this.commissionFontColor;
data['commission_bg_color'] = this.commissionBgColor;
data['commission_bg_img'] = this.commissionBgImg;
data['is_img'] = this.isImg;
return data;
}
}

class Bottom {
String bgColor;
String leftText;
String leftColor;
String rightText;
String rightColor;

Bottom({this.bgColor, this.leftText, this.leftColor, this.rightText, this.rightColor});

Bottom.fromJson(Map<String, dynamic> json) {
bgColor = json['bg_color'];
leftText = json['left_text'];
leftColor = json['left_color'];
rightText = json['right_text'];
rightColor = json['right_color'];
}

Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['bg_color'] = this.bgColor;
data['left_text'] = this.leftText;
data['left_color'] = this.leftColor;
data['right_text'] = this.rightText;
data['right_color'] = this.rightColor;
return data;
}
}

class ConfirmDialog {
String bgText;
String bgTextColor;
String bgColor;
String leftText;
String leftTextColor;
String leftBtnBgColor;
String rightText;
String rightTextColor;
String rightBtnBgColor;

ConfirmDialog({this.bgText, this.bgTextColor, this.bgColor, this.leftText, this.leftTextColor, this.leftBtnBgColor, this.rightText, this.rightTextColor, this.rightBtnBgColor});

ConfirmDialog.fromJson(Map<String, dynamic> json) {
bgText = json['bg_text'];
bgTextColor = json['bg_text_color'];
bgColor = json['bg_color'];
leftText = json['left_text'];
leftTextColor = json['left_text_color'];
leftBtnBgColor = json['left_btn_bg_color'];
rightText = json['right_text'];
rightTextColor = json['right_text_color'];
rightBtnBgColor = json['right_btn_bg_color'];
}

Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['bg_text'] = this.bgText;
data['bg_text_color'] = this.bgTextColor;
data['bg_color'] = this.bgColor;
data['left_text'] = this.leftText;
data['left_text_color'] = this.leftTextColor;
data['left_btn_bg_color'] = this.leftBtnBgColor;
data['right_text'] = this.rightText;
data['right_text_color'] = this.rightTextColor;
data['right_btn_bg_color'] = this.rightBtnBgColor;
return data;
}
}

+ 44
- 0
lib/pages/favorites_page/notifier/favorites_page_notifier.dart ファイルの表示

@@ -0,0 +1,44 @@
import 'package:flutter/material.dart';
import 'package:zhiying_comm/util/empty_util.dart';

class FavoritesPageNotifier with ChangeNotifier {
/// 是否是编辑中
bool isEditing = false;

/// 搜索关键字
String keyword;

int selectIndex = 0;

/// 用于区分是编辑还是搜索
bool editingTypeTag = true;

/// 用于区分是否修改
// int changeVal = 0;

/// 选中的下标
void changeSelectIndex(int index){
this.selectIndex = index;
// ++changeVal;
notifyListeners();
}

/// 编辑
void editing() {
this.editingTypeTag = true;
this.isEditing = !isEditing;
// ++changeVal;
notifyListeners();
}

/// 搜索
void search(String keyword) {
// if (!EmptyUtil.isEmpty(keyword)) {
this.editingTypeTag = false;
this.keyword = keyword;
// ++changeVal;
notifyListeners();
// }
}

}

+ 6
- 2
lib/register.dart ファイルの表示

@@ -1,6 +1,6 @@
import 'package:sharesdk_plugin/sharesdk_interface.dart';
import 'package:sharesdk_plugin/sharesdk_register.dart';
import 'package:zhiying_base_widget/pages/favorite_page/favorite_page.dart';
import 'package:zhiying_base_widget/pages/feedback_page/feedback_page.dart';
import 'package:zhiying_base_widget/pages/goods_details_page/goods_details_page.dart';
import 'package:zhiying_base_widget/pages/home_page/home_page.dart';
@@ -52,6 +52,7 @@ import 'package:zhiying_base_widget/widgets/wallet/wallet_income/wallet_income.d
import 'package:zhiying_comm/util/defalut_widget_creater.dart';
import 'package:zhiying_comm/zhiying_comm.dart';

import 'pages/favorites_page/favorites_page.dart';
import 'pages/message_notice_page/message_notice_page.dart';
import 'pages/search_page/search_page.dart';
import 'pages/wallet_page/wallet_page.dart';
@@ -142,7 +143,7 @@ class BaseWidgetRegister {
// 邀请好友
PageFactory.regist(
'pub.flutter.invite_friends', (model) => InvitedFriendsPage(model));
PageFactory.regist('pub.flutter.fav', (model) => FavoritePage());

/// 我的团队
PageFactory.regist('pub.flutter.my_team', (model) => TeamPage(model));
@@ -154,6 +155,9 @@ class BaseWidgetRegister {
/// 消息中心
PageFactory.regist(
'pub.flutter.message_center', (model) => MessageNoticePage(model));

/// 我的收藏
PageFactory.regist('pub.flutter.my_fav', (model) => FavoritesPage());
}

// 注册控件


+ 75
- 0
lib/widgets/favorites/dialog/favorites_dialog_widget.dart ファイルの表示

@@ -0,0 +1,75 @@
import 'package:flutter/material.dart';
import 'package:zhiying_base_widget/pages/favorites_page/model/favorites_style_model.dart';
import 'package:zhiying_comm/zhiying_comm.dart';

class FavoritesDialogWidget extends StatelessWidget {
ConfirmDialog styleModel;

FavoritesDialogWidget({@required this.styleModel});

@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.transparent,
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
width: double.infinity,
padding: const EdgeInsets.only(top: 30, left: 17, right: 17, bottom: 12.5),
decoration: BoxDecoration(color: HexColor.fromHex(styleModel?.bgColor ?? '#FFFFFF'), borderRadius: BorderRadius.circular(8)),
margin: EdgeInsets.only(left: 46, right: 46),
child: Column(
children: <Widget>[
/// 标题
Text(styleModel?.bgText, style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold, color: HexColor.fromHex(styleModel?.bgTextColor ?? '#333333'))),
const SizedBox(height: 44),
Row(
children: <Widget>[
Flexible(flex: 1, child: _buildCustomerButtonWidget(
text: styleModel?.leftText ?? '我在想想',
textColor:styleModel?.leftTextColor ?? '#FF4242',
bgColor: styleModel?.leftBtnBgColor ?? '#FFFFFF',
borderColor: styleModel?.leftTextColor ?? '#FF4242',
callback: ()=> Navigator.maybePop(context, 0),
)),
const SizedBox(width: 14),
Flexible(flex: 1, child: _buildCustomerButtonWidget(
text: styleModel?.rightText ?? '确认删除',
textColor: styleModel?.rightTextColor ?? '#FFFFFF',
borderColor: styleModel?.rightTextColor ?? '#FFFFFF',
bgColor: styleModel?.rightBtnBgColor ?? '#FF4242',
callback: ()=> Navigator.maybePop(context, 1),
)),
],
)
],
),
)
],
),
);
}

Widget _buildCustomerButtonWidget({String text, String textColor, String bgColor, String borderColor,GestureTapCallback callback, double height = 30}) {
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: callback,
child: Container(
alignment: Alignment.center,
height: height,
width: double.infinity,
decoration: BoxDecoration(
color: HexColor.fromHex(bgColor),
borderRadius: BorderRadius.circular(height / 2),
border: Border.all(color: HexColor.fromHex(borderColor), width: 0.5),
),
child: Text(
text ?? '',
style: TextStyle(color: HexColor.fromHex(textColor), fontSize: 13),
),
),
);
}
}

+ 173
- 0
lib/widgets/favorites/goods_list/bloc/favorites_goods_list_bloc.dart ファイルの表示

@@ -0,0 +1,173 @@
import 'dart:async';

import 'package:bloc/bloc.dart';
import 'package:flutter/material.dart';
import 'package:meta/meta.dart';
import 'package:zhiying_base_widget/widgets/favorites/goods_list/model/favorites_goods_list_model.dart';
import 'package:zhiying_base_widget/widgets/home/home_goods/models/home_goods_model.dart';
import 'package:zhiying_comm/util/empty_util.dart';

import 'favorites_goods_list_repository.dart';

part 'favorites_goods_list_event.dart';

part 'favorites_goods_list_state.dart';

class FavoritesGoodsListBloc extends Bloc<FavoritesGoodsListEvent, FavoritesGoodsListState> {
@override
FavoritesGoodsListState get initialState => FavoritesGoodsListInitial();

FavoritesGoodsListRepository repository;

FavoritesGoodsListBloc(this.repository);

@override
Stream<FavoritesGoodsListState> mapEventToState(
FavoritesGoodsListEvent event,
) async* {
/// 初始化数据
if (event is FavoritesGoodsListInitEvent) {
yield* _mapInitEventToState(event);
}

/// 下拉刷新
if (event is FavoritesGoodsListOnRefreshEvent) {
yield* _mapOnRefreshEventToState(event);
}

/// 上拉更多
if (event is FavoritesGoodsListOnLoadEvent) {
yield* _mapOnLoadEventToState(event);
}

/// 删除
if (event is FavoritesGoodsListDeleteEvent) {
yield* _mapDeleteEventToState(event);
}

/// 搜索
if (event is FavoritesGoodsListSearchEvent) {
yield* _mapSearchEventToState(event);
}

/// 单个商品选中
if(event is FavoritesGoodsListSignGoodsSelectEvent){
yield* _mapSelectSignEventToState(event);
}

/// 所有商品选中
if(event is FavoritesGoodsListGoodsSelectAllEvent){
yield* _mapSelectAllEventToState(event);
}

if(event is FavoritesGoodsListGoodsCancelAllEvent){
yield* _mapCancelAllEventToState(event);
}

// /// 切换样式
// if (event is FavoritesGoodsListChangeStyleEvent) {
// yield* _mapChangeStyleEventToState(event);
// }
}

/// 初始化数据
Stream<FavoritesGoodsListState> _mapInitEventToState(FavoritesGoodsListInitEvent event) async* {
var result = await repository.fetchInit();
if (!EmptyUtil.isEmpty(result)) {
yield FavoritesGoodsListLoadedState(dataModel: result);
} else {
yield FavoritesGoodsListInitErrorState();
}
}

/// 下拉刷新
Stream<FavoritesGoodsListState> _mapOnRefreshEventToState(FavoritesGoodsListOnRefreshEvent event) async* {
var result = await repository.fetchRefresh();
if (!EmptyUtil.isEmpty(result)) {
yield FavoritesGoodsListRefreshSuccessState();
yield FavoritesGoodsListLoadedState(dataModel: result);
} else {
yield FavoritesGoodsListRefreshErrorState();
yield FavoritesGoodsListErrorState();
}
}

/// 上拉更多
Stream<FavoritesGoodsListState> _mapOnLoadEventToState(FavoritesGoodsListOnLoadEvent event) async* {
var result = await repository.fetchLoad();
if (!EmptyUtil.isEmpty(result)) {
yield FavoritesGoodsListLoadSuccessState();
yield FavoritesGoodsListLoadedState(dataModel: result);
} else {
yield FavoritesGoodsListLoadErrorState();
yield FavoritesGoodsListErrorState();
}
}

/// 删除
Stream<FavoritesGoodsListState> _mapDeleteEventToState(FavoritesGoodsListDeleteEvent event) async* {
int length = repository.getSelectLength();
if(length > 0) {
var result = await repository.delete();
if (!EmptyUtil.isEmpty(result)) {
yield FavoritesGoodsListLoadedState(dataModel: result);
} else {
yield FavoritesGoodsListErrorState();
}
}else{
yield FavoritesGoodsListNotSelectState();
}
}

/// 搜索
Stream<FavoritesGoodsListState> _mapSearchEventToState(FavoritesGoodsListSearchEvent event) async* {
var result = await repository.search(event.keyword);
if (!EmptyUtil.isEmpty(result)) {
yield FavoritesGoodsListLoadedState(dataModel: result);
} else {
yield FavoritesGoodsListInitErrorState();
}
}

/// 所有商品选中
Stream<FavoritesGoodsListState> _mapSelectAllEventToState(FavoritesGoodsListGoodsSelectAllEvent event) async*{
var result = await repository.selectAllData();
if (!EmptyUtil.isEmpty(result)) {
yield FavoritesGoodsListLoadedState(dataModel: result);
} else {
yield FavoritesGoodsListErrorState();
}
}

/// 商品的全部取消
Stream<FavoritesGoodsListState> _mapCancelAllEventToState(FavoritesGoodsListGoodsCancelAllEvent event) async*{
var result = await repository.cancelSelectAllData();
if (!EmptyUtil.isEmpty(result)) {
yield FavoritesGoodsListLoadedState(dataModel: result);
} else {
yield FavoritesGoodsListErrorState();
}
}

/// 单个商品选中
Stream<FavoritesGoodsListState> _mapSelectSignEventToState(FavoritesGoodsListSignGoodsSelectEvent event) async*{
var result = await repository.signSelectData(event.index);
if (!EmptyUtil.isEmpty(result)) {
yield FavoritesGoodsListLoadedState(dataModel: result);
} else {
yield FavoritesGoodsListErrorState();
}
}



// /// 切换样式
// Stream<FavoritesGoodsListState> _mapChangeStyleEventToState(FavoritesGoodsListChangeStyleEvent event) async* {
// var result = await repository.fetchInit();
// if (!EmptyUtil.isEmpty(result)) {
// yield FavoritesGoodsListLoadedState(dataModel: result);
// } else {
// yield FavoritesGoodsListInitErrorState();
// }
// }
}

+ 39
- 0
lib/widgets/favorites/goods_list/bloc/favorites_goods_list_event.dart ファイルの表示

@@ -0,0 +1,39 @@
part of 'favorites_goods_list_bloc.dart';

@immutable
abstract class FavoritesGoodsListEvent {}


/// 初始化数据
class FavoritesGoodsListInitEvent extends FavoritesGoodsListEvent {}

/// 下拉刷新
class FavoritesGoodsListOnRefreshEvent extends FavoritesGoodsListEvent {}

/// 上拉更多
class FavoritesGoodsListOnLoadEvent extends FavoritesGoodsListEvent {}

/// 关键字搜索
class FavoritesGoodsListSearchEvent extends FavoritesGoodsListEvent {
final String keyword;
FavoritesGoodsListSearchEvent(this.keyword);
}

/// 删除
class FavoritesGoodsListDeleteEvent extends FavoritesGoodsListEvent {}

/// 切换样式
class FavoritesGoodsListChangeStyleEvent extends FavoritesGoodsListEvent{}

/// 商品的当个选中
class FavoritesGoodsListSignGoodsSelectEvent extends FavoritesGoodsListEvent{
// final FavoritesGoodsListItemModel model;
final int index;
FavoritesGoodsListSignGoodsSelectEvent(this.index);
}

/// 商品的全选
class FavoritesGoodsListGoodsSelectAllEvent extends FavoritesGoodsListEvent{}

/// 商品的全部取消
class FavoritesGoodsListGoodsCancelAllEvent extends FavoritesGoodsListEvent{}

+ 161
- 0
lib/widgets/favorites/goods_list/bloc/favorites_goods_list_repository.dart ファイルの表示

@@ -0,0 +1,161 @@
import 'package:flutter/cupertino.dart';
import 'package:zhiying_base_widget/widgets/favorites/goods_list/model/favorites_goods_list_model.dart';
import 'package:zhiying_base_widget/widgets/home/home_goods/models/home_goods_model.dart';
import 'package:zhiying_comm/zhiying_comm.dart';

class FavoritesGoodsListRepository {
String type;

final int _MAX = 5;
int _currentPage = 1;
bool _hasMoreData = true;

FavoritesGoodsListDataModel _oldData = new FavoritesGoodsListDataModel();
Map<int, int> _selectIndex = {};

FavoritesGoodsListRepository({this.type = 'all'});

String keyword;

/// 返回选中的商品长度
int getSelectLength() {
return _selectIndex.length;
}

/// 选中所有
Future<FavoritesGoodsListDataModel> selectAllData() async {
_selectIndex.clear();
int index = 0;
_oldData.lists.forEach((element) {
element.isSelect = true;
_selectIndex[index] = index;
++index;
});
_oldData.isAllSelect = true;
return _oldData;
}

/// 单个选中状态
Future<FavoritesGoodsListDataModel> signSelectData(final int selectIndex) async {
_oldData.lists[selectIndex].isSelect = !_oldData.lists[selectIndex].isSelect;

if (_oldData.lists[selectIndex].isSelect) {
// 新增
_selectIndex[selectIndex] = selectIndex;
} else {
// 移除
_selectIndex.remove(selectIndex);
}
_oldData.isAllSelect = false;
if (_selectIndex.length == _oldData.lists.length) {
_oldData.isAllSelect = true;
}
return _oldData;
}

/// 取消选中所有 or 重置选中状态
Future<FavoritesGoodsListDataModel> cancelSelectAllData() async {
_selectIndex.clear();
_oldData.lists.forEach((element) {
element.isSelect = false;
});
_oldData.isAllSelect = false;
return _oldData;
}

/// 关键字搜索
Future<FavoritesGoodsListDataModel> search(String keyword) async {
try {
_selectIndex.clear();
_oldData.lists.clear();
_currentPage = 1;
_hasMoreData = true;

this.keyword = keyword;
return _baseRequest();
} catch (e, s) {
Logger.error(e, s);
}
return null;
}

/// 删除数据
Future<FavoritesGoodsListDataModel> delete() async {
try {
// Logger.log('select Index length = ${_selectIndex.length}');
if (_selectIndex.length > 0) {
List<String> gids = [];
_selectIndex.forEach((key, value) {
gids.add(_oldData.lists[value].goodId);
});
var result = await NetUtil.post('/api/v1/user/myfav/delete', params: {'gids': gids}, method: NetMethod.POST);
if (NetUtil.isSuccess(result)) {
List<FavoritesGoodsListItemModel> tempList = [];
// TODO 这里有问题:移除删除
for (int i = 0; i < _oldData.lists.length; i++) {
if (!_selectIndex.containsKey(i)) {
tempList.add(_oldData.lists[i]);
}
}
_oldData.lists.clear();
_selectIndex.clear();
_oldData.isAllSelect = false;
_oldData.lists = tempList;
return _oldData;
}
}
} catch (e, s) {
Logger.error(e, s);
}
return null;
}

/// 初始化请求
Future<FavoritesGoodsListDataModel> fetchInit() async {
_currentPage = 1;
_hasMoreData = true;
_oldData?.lists?.clear();
_selectIndex.clear();
_oldData.isAllSelect = false;
return _baseRequest();
}

/// 下拉刷新
Future<FavoritesGoodsListDataModel> fetchRefresh() async {
return fetchInit();
}

/// 上拉更多
Future<FavoritesGoodsListDataModel> fetchLoad() async {
if (_hasMoreData) {
return _baseRequest();
}
return null;
}

/// 基础请求
Future<FavoritesGoodsListDataModel> _baseRequest() async {
try {
var result;
if(EmptyUtil.isEmpty(keyword)) {
result = await NetUtil.post('/api/v1/user/myfav/$type?page=${_currentPage.toString()}', params: {}, method: NetMethod.GET);
}else{
result = await NetUtil.post('/api/v1/user/myfav/s', params: {'keyword': keyword}, method: NetMethod.POST);
}
if (NetUtil.isSuccess(result) && !EmptyUtil.isEmpty(result[GlobalConfig.HTTP_RESPONSE_KEY_DATA])) {
List data = result[GlobalConfig.HTTP_RESPONSE_KEY_DATA];
data.forEach((element) {
_oldData.lists.add(FavoritesGoodsListItemModel.fromJson(element));
});
++_currentPage;
_hasMoreData = true;
// _hasMoreData = data.length >= _MAX ? true : false;
return _oldData;
}
_hasMoreData = false;
} catch (e, s) {
Logger.error(e, s);
}
return null;
}
}

+ 35
- 0
lib/widgets/favorites/goods_list/bloc/favorites_goods_list_state.dart ファイルの表示

@@ -0,0 +1,35 @@
part of 'favorites_goods_list_bloc.dart';

@immutable
abstract class FavoritesGoodsListState {}

/// 初始化状态(骨架图)
class FavoritesGoodsListInitial extends FavoritesGoodsListState {}

/// 初始化失败状态(空数据)
class FavoritesGoodsListInitErrorState extends FavoritesGoodsListState {}

/// 下拉刷新失败状态
class FavoritesGoodsListRefreshErrorState extends FavoritesGoodsListState {}

/// 下拉刷新成功状态
class FavoritesGoodsListRefreshSuccessState extends FavoritesGoodsListState {}

/// 上拉更多失败状态
class FavoritesGoodsListLoadErrorState extends FavoritesGoodsListState {}

/// 上拉更多成功状态
class FavoritesGoodsListLoadSuccessState extends FavoritesGoodsListState {}

/// 数据加载成功
class FavoritesGoodsListLoadedState extends FavoritesGoodsListState {
// List<FavoritesGoodsListItemModel> dataModel;
FavoritesGoodsListDataModel dataModel;
FavoritesGoodsListLoadedState({@required this.dataModel});
}

/// 没选中
class FavoritesGoodsListNotSelectState extends FavoritesGoodsListState{}

/// 未知错误
class FavoritesGoodsListErrorState extends FavoritesGoodsListState {}

+ 325
- 0
lib/widgets/favorites/goods_list/favorites_goods_list_widget.dart ファイルの表示

@@ -0,0 +1,325 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';
import 'package:zhiying_base_widget/dialog/tip_dialog/tip_dialog.dart';
import 'package:zhiying_base_widget/pages/favorites_page/model/favorites_style_model.dart';
import 'package:zhiying_base_widget/pages/favorites_page/notifier/favorites_page_notifier.dart';
import 'package:zhiying_base_widget/widgets/favorites/dialog/favorites_dialog_widget.dart';
import 'package:zhiying_base_widget/widgets/favorites/goods_list/bloc/favorites_goods_list_bloc.dart';
import 'package:zhiying_base_widget/widgets/favorites/goods_list/bloc/favorites_goods_list_repository.dart';
import 'package:zhiying_base_widget/widgets/home/home_goods/home_goods_item_single.dart';
import 'package:zhiying_base_widget/widgets/home/home_goods/models/home_goods_style_model.dart';
import 'package:zhiying_base_widget/widgets/home/home_goods/skeleton/home_goods_sk.dart';
import 'package:zhiying_comm/zhiying_comm.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:provider/provider.dart';
import 'package:fluttertoast/fluttertoast.dart';

import 'model/favorites_goods_list_model.dart';

class FavoritesGoodsListWidget extends StatelessWidget {
FavoritesStyleModel model;
String type;
int currentIndex;

FavoritesGoodsListWidget({@required this.model, @required this.type, this.currentIndex = 0});

@override
Widget build(BuildContext context) {
return BlocProvider<FavoritesGoodsListBloc>(
create: (_) => FavoritesGoodsListBloc(FavoritesGoodsListRepository(type: type)),
child: _FavoritesGoodsListWidgetContainer(model, currentIndex),
);
}
}

class _FavoritesGoodsListWidgetContainer extends StatefulWidget {
FavoritesStyleModel styleModel;
int currentIndex;

_FavoritesGoodsListWidgetContainer(this.styleModel, this.currentIndex);

@override
__FavoritesGoodsListWidgetContainerState createState() => __FavoritesGoodsListWidgetContainerState();
}

class __FavoritesGoodsListWidgetContainerState extends State<_FavoritesGoodsListWidgetContainer> with AutomaticKeepAliveClientMixin {
RefreshController _refreshController;

// 是否编辑中
bool isEditing = false;
int lastChangeVal = -1;

/// 下拉刷新
void _onRefresh() async {
BlocProvider.of<FavoritesGoodsListBloc>(context).add(FavoritesGoodsListOnRefreshEvent());
}

/// 上拉更多
void _onLoad() async {
BlocProvider.of<FavoritesGoodsListBloc>(context).add(FavoritesGoodsListOnLoadEvent());
}

/// 商品点击选中
void _onClickGoods(int index){
BlocProvider.of<FavoritesGoodsListBloc>(context).add(FavoritesGoodsListSignGoodsSelectEvent(index));
}

/// 全选
void _onClickAllSelecct(FavoritesGoodsListDataModel dataModel) {

if(!dataModel.isAllSelect) {
BlocProvider.of<FavoritesGoodsListBloc>(context).add(FavoritesGoodsListGoodsSelectAllEvent());
}else{
BlocProvider.of<FavoritesGoodsListBloc>(context).add(FavoritesGoodsListGoodsCancelAllEvent());
}
}

/// 点击删除
void _onClickDelete(ConfirmDialog styleModel) async {

//
var result = await showDialog(context: context, child: FavoritesDialogWidget(styleModel: styleModel,));
if(result == 1){
BlocProvider.of<FavoritesGoodsListBloc>(context).add(FavoritesGoodsListDeleteEvent());
}
}

@override
bool get wantKeepAlive => true;

@override
void initState() {
_refreshController = RefreshController();
BlocProvider.of<FavoritesGoodsListBloc>(context).add(FavoritesGoodsListInitEvent());
super.initState();
}

@override
void didChangeDependencies() {
super.didChangeDependencies();
FavoritesPageNotifier favoritesPageNotifier = Provider.of<FavoritesPageNotifier>(context);

if (null != favoritesPageNotifier) {
isEditing = favoritesPageNotifier.isEditing;
if(favoritesPageNotifier.editingTypeTag) {

// BlocProvider.of<FavoritesGoodsListBloc>(context).add(FavoritesGoodsListGoodsCancelAllEvent());
}else{
// if(favoritesPageNotifier.selectIndex == widget.currentIndex) {
BlocProvider.of<FavoritesGoodsListBloc>(context).add(FavoritesGoodsListSearchEvent(favoritesPageNotifier?.keyword));
// }
}
}
}

@override
void dispose() {
_refreshController?.dispose();
super.dispose();
}

@override
Widget build(BuildContext context) {
return Listener(
onPointerDown: (down)=> RouterUtil.hideKeyboard(context),
child: Container(
color: HexColor.fromHex('#F9F9F9'),
child: BlocConsumer<FavoritesGoodsListBloc, FavoritesGoodsListState>(
listener: (context, state) {},
buildWhen: (prev, current) {
if (current is FavoritesGoodsListRefreshSuccessState) {
_refreshController?.refreshCompleted(resetFooterState: true);
return false;
}
if (current is FavoritesGoodsListRefreshErrorState) {
_refreshController?.refreshFailed();
return false;
}
if (current is FavoritesGoodsListLoadSuccessState) {
_refreshController?.loadComplete();
return false;
}
if (current is FavoritesGoodsListLoadErrorState) {
_refreshController?.loadNoData();
return false;
}
if (current is FavoritesGoodsListErrorState) {
return false;
}
if(current is FavoritesGoodsListNotSelectState){
Fluttertoast.showToast(msg: '请选择需要删除的商品~');
return false;
}
return true;
},
builder: (context, state) {
/// 初始化失败,空数据
if (state is FavoritesGoodsListInitErrorState) {
return _buildEmptyWidget(widget?.styleModel);
}

/// 数据加载成功
if (state is FavoritesGoodsListLoadedState) {
// Logger.log('__FavoritesGoodsListWidgetContainerState ==== 刷新 size = ${state?.dataModel?.lists?.length}');
return _buildGoodsListWidget(widget?.styleModel, state?.dataModel);
}

/// 骨架图
return _buildSkeletonWidget(widget?.styleModel);
},
),
),
);
}

/// 有数据返回
Widget _buildGoodsListWidget(FavoritesStyleModel styleModel, FavoritesGoodsListDataModel dataModel) {


/// 如果数据删除了,则返回空视图
int length = dataModel?.lists?.length ?? 0;
if(length == 0) return _buildEmptyWidget(styleModel);

return Stack(
children: <Widget>[
/// 数据
SmartRefresher(
controller: _refreshController,
onRefresh: _onRefresh,
onLoading: _onLoad,
enablePullUp: true,
enablePullDown: true,
child: ListView.builder(
padding: EdgeInsets.only(top: 7.5, left: 12.5, right: isEditing ? 0 : 12.5),
itemCount: dataModel.lists.length,
itemBuilder: (context, index) {
FavoritesGoodsListItemModel item = dataModel.lists[index];
return Row(
children: <Widget>[
Visibility(
visible: isEditing ,
child: GestureDetector(
onTap: ()=> _onClickGoods(index),
behavior: HitTestBehavior.opaque,
child: Container(
height: 20,
width: 20,
margin: const EdgeInsets.only(right: 8),
child: CachedNetworkImage(
imageUrl: item.isSelect ? styleModel?.editLeftConfirmIcon : styleModel?.editLeftNoConfirmIcon,
),
),
),
),
Expanded(
child: HomeGoodsItemSingle(
item,
HomeGoodsStyleModel()..listStyle = styleModel?.listStyle,
marginBottom: 0,
marginLeft: 0,
marginRight: 0,
marginTop: 0,
),
)
],
);
}),
),

/// 编辑
_buildEditrWidget(widget?.styleModel, dataModel),
],
);
}

/// 编辑的底部Widget
Widget _buildEditrWidget(FavoritesStyleModel styleModel, FavoritesGoodsListDataModel dataModel) {
return Align(
alignment: Alignment.bottomCenter,
child: Visibility(
visible: isEditing ,
child: Container(
padding: const EdgeInsets.only(left: 12.5, right: 12.5, top: 15.5, bottom: 15.5),
width: double.infinity,
decoration: BoxDecoration(
color: HexColor.fromHex(styleModel?.bottom?.bgColor ?? '#FFFFFF'), borderRadius: BorderRadius.only(topRight: Radius.circular(10), topLeft: Radius.circular(10))),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
GestureDetector(
onTap: () => _onClickAllSelecct(dataModel),
behavior: HitTestBehavior.opaque,
child: Row(
children: <Widget>[
Visibility(
visible: dataModel?.isAllSelect ?? false,
replacement: Container(
width: 20,
height: 20,
decoration: BoxDecoration(borderRadius: BorderRadius.circular(20), border: Border.all(color: HexColor.fromHex('#DCDCDC'), width: 0.5)),
),
child: Container(
height: 20,
width: 20,
child: CachedNetworkImage(imageUrl: styleModel?.editLeftConfirmIcon ?? '',),
),
),
const SizedBox(width: 8),
Text(
styleModel?.bottom?.leftText ?? '全选',
style: TextStyle(
fontSize: 15,
color: HexColor.fromHex(styleModel?.bottom?.leftColor ?? '#333333'),
fontWeight: FontWeight.bold,
),
),
],
),
),
GestureDetector(
onTap: () => _onClickDelete(styleModel?.confirmDialog),
behavior: HitTestBehavior.opaque,
child: Text(
styleModel?.bottom?.rightText ?? '删除',
style: TextStyle(
fontSize: 15,
color: HexColor.fromHex(styleModel?.bottom?.rightColor ?? '#FF4242'),
fontWeight: FontWeight.bold,
),
),
),
],
),
),
),
);
}

/// 空数据
Widget _buildEmptyWidget(FavoritesStyleModel styleModel) {
return SmartRefresher(
controller: _refreshController,
onRefresh: _onRefresh,
enablePullDown: true,
enablePullUp: false,
child: Column(
children: <Widget>[
const SizedBox(height: 80),
CachedNetworkImage(imageUrl: styleModel?.tabItemNotFoundImg ?? '', width: 188),
const SizedBox(height: 22),
Text(
'暂时没有商品噢\n快去浏览收藏吧',
style: TextStyle(fontSize: 15, color: HexColor.fromHex('#999999')),
)
],
),
);
}

/// 骨架图
Widget _buildSkeletonWidget(FavoritesStyleModel styleModel) {
return HomeGoodsSkeleton();
}
}

+ 37
- 0
lib/widgets/favorites/goods_list/model/favorites_goods_list_model.dart ファイルの表示

@@ -0,0 +1,37 @@
import 'package:zhiying_base_widget/widgets/home/home_goods/models/home_goods_model.dart';

// ignore: must_be_immutable

class FavoritesGoodsListDataModel {
// 是否全选
bool isAllSelect = false;

List<FavoritesGoodsListItemModel> lists = [];

// FavoritesGoodsListDataModel({this.isEditing, this.isAllSelect, this.lists });
FavoritesGoodsListDataModel();

FavoritesGoodsListDataModel.fromJson(Map<String, dynamic> json) {
isAllSelect = json['isAllSelect'] ?? false;
if(json['data'] != null){
json['data'].forEach((v){
lists.add(new FavoritesGoodsListItemModel.fromJson(v));
});
}
}
}

class FavoritesGoodsListItemModel extends HomeGoodsModel {
bool isSelect;

FavoritesGoodsListItemModel.fromJson(Map<String, dynamic> json) {
super.fromJson(json);
isSelect = json['isSelect'] ?? false;
}

Map<String, dynamic> toJson() {
final Map<String, dynamic> data = super.toJson();
data['isSelect'] = this.isSelect;
return data;
}
}

+ 23
- 2
lib/widgets/goods_details/footer/bloc/goods_details_footer_bloc.dart ファイルの表示

@@ -27,11 +27,22 @@ class GoodsDetailsFooterBloc extends Bloc<GoodsDetailsFooterEvent, GoodsDetailsF
if (event is GoodsDetailsFooterInitEvent) {
yield* _mapInitEventToState(event);
}

/// 收藏
if(event is GoodsDetailsFooterCollectEvent){
yield* _mapCollectEventToState(event);
}

/// 取消收藏
if(event is GoodsDetailsFooterDeleteCollectEvent){
yield* _mapDeleteCollectEventToState(event);
}
}

/// 初始化
Stream<GoodsDetailsFooterState> _mapInitEventToState(GoodsDetailsFooterInitEvent event) async* {
var result = await repository.fetchParentData(event.model);
var result = await repository.fetchParentData(event?.model);
if (!EmptyUtil.isEmpty(result)) {
print('result = ${result.toString()}');
yield GoodsDetailsFooterLoadedState(model: result);
@@ -42,10 +53,20 @@ class GoodsDetailsFooterBloc extends Bloc<GoodsDetailsFooterEvent, GoodsDetailsF

/// 点击收藏
Stream<GoodsDetailsFooterState> _mapCollectEventToState(GoodsDetailsFooterCollectEvent event) async* {
var result = await repository.fetchCollect();
var result = await repository.fetchCollect(event?.model);
if (!EmptyUtil.isEmpty(result))
yield GoodsDetailsFooterLoadedState(model: result);
else
yield GoodsDetailsFooterErrorState();
}
/// 取消收藏
Stream<GoodsDetailsFooterState> _mapDeleteCollectEventToState(GoodsDetailsFooterDeleteCollectEvent event ) async*{
var result = await repository.fetchDeleteCollect(event?.model);
if(!EmptyUtil.isEmpty(result))
yield GoodsDetailsFooterLoadedState(model: result);
else
yield GoodsDetailsFooterErrorState();
}
}

+ 15
- 0
lib/widgets/goods_details/footer/bloc/goods_details_footer_event.dart ファイルの表示

@@ -19,5 +19,20 @@ class GoodsDetailsFooterInitEvent extends GoodsDetailsFooterEvent {

/// 点击收藏
class GoodsDetailsFooterCollectEvent extends GoodsDetailsFooterEvent {
final Map<String, dynamic> model;

GoodsDetailsFooterCollectEvent({@required this.model});

@override
List<Object> get props => [this.model];
}

/// 取消收藏
class GoodsDetailsFooterDeleteCollectEvent extends GoodsDetailsFooterEvent {
final Map<String, dynamic> model;

GoodsDetailsFooterDeleteCollectEvent({@required this.model});

@override
List<Object> get props => [this.model];
}

+ 36
- 4
lib/widgets/goods_details/footer/bloc/goods_details_footer_repository.dart ファイルの表示

@@ -4,18 +4,50 @@ import 'package:zhiying_base_widget/widgets/goods_details/footer/model/goods_det
import 'package:zhiying_comm/zhiying_comm.dart';

class GoodsDetailsFooterRepository {
GoodsDetailsFooterModel _oldmodel;
/// 初始化数据
Future<GoodsDetailsFooterModel> fetchParentData(final Map<String, dynamic> model) async {
try {
return GoodsDetailsFooterModel.fromJson(jsonDecode(model['data']));
} catch (e) {
Logger.log('GoodsDetailsFooterRepository e = $e');
GoodsDetailsFooterModel data = GoodsDetailsFooterModel.fromJson(jsonDecode(model['data']));
_oldmodel = data;
return data;
} catch (e, s) {
Logger.error(e, s);
}
return null;
}

/// 点击收藏
Future<GoodsDetailsFooterModel> fetchCollect() async{
Future<GoodsDetailsFooterModel> fetchCollect(final Map<String, dynamic> model) async {
try {
if (!EmptyUtil.isEmpty(model)) {
var result = await NetUtil.post('/api/v1/user/myfav', params: model, method: NetMethod.POST);
if(NetUtil.isSuccess(result)){
_oldmodel.isFav = '1';
return _oldmodel;
}
}
} catch (e, s) {
Logger.error(e, s);
}
return null;
}

/// 取消收藏
Future<GoodsDetailsFooterModel> fetchDeleteCollect(final Map<String, dynamic> model) async{
try{
if(!EmptyUtil.isEmpty(model)){
var result = await NetUtil.post('/api/v1/user/myfav/delete', params: {'gids':[model['good_id']]}, method: NetMethod.POST);
if(NetUtil.isSuccess(result)){
_oldmodel.isFav = '0';
return _oldmodel;
}
}
}catch(e, s){
Logger.error(e,s);
}
return null;
}



+ 7
- 7
lib/widgets/goods_details/footer/bloc/goods_details_footer_state.dart ファイルの表示

@@ -1,15 +1,15 @@
part of 'goods_details_footer_bloc.dart';

abstract class GoodsDetailsFooterState extends Equatable {
abstract class GoodsDetailsFooterState {
const GoodsDetailsFooterState();

@override
List<Object> get props => [];
// @override
// List<Object> get props => [];
}

class GoodsDetailsFooterInitial extends GoodsDetailsFooterState {
@override
List<Object> get props => [];
// @override
// List<Object> get props => [];
}

/// 数据加载完毕
@@ -18,8 +18,8 @@ class GoodsDetailsFooterLoadedState extends GoodsDetailsFooterState {

GoodsDetailsFooterLoadedState({@required this.model});

@override
List<Object> get props => [this.model];
// @override
// List<Object> get props => [this.model];
}

/// 数据加载出错


+ 10
- 3
lib/widgets/goods_details/footer/goods_details_footer_widget.dart ファイルの表示

@@ -75,7 +75,14 @@ class _GooddsDetailsFooterContainerState
}

/// 收藏
void _collectOnClick() {}
void _collectOnClick(GoodsDetailsFooterModel model) {
bool isCollect = model.isFav == '0';
if(isCollect){ // 收藏
BlocProvider.of<GoodsDetailsFooterBloc>(context).add(GoodsDetailsFooterCollectEvent(model: model?.favArgs?.toJson()));
}else{ // 取消收藏
BlocProvider.of<GoodsDetailsFooterBloc>(context).add(GoodsDetailsFooterDeleteCollectEvent(model: model?.favArgs?.toJson()));
}
}

/// 分享
void _shareOnClick(GoodsDetailsFooterModel model) async {
@@ -221,13 +228,13 @@ class _GooddsDetailsFooterContainerState
)),
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () => _collectOnClick(),
onTap: () => _collectOnClick(model),
child: Padding(
padding: const EdgeInsets.only(right: 0),
child: _getCustomWidget(
model?.collect ?? '收藏',
model?.collect_color ?? '999999',
model?.collect_icon ?? '')))
model?.isFav == '0' ? model?.collect_icon ?? '' : model?.collected_icon ?? '')))
],
);
}


+ 153
- 19
lib/widgets/goods_details/footer/model/goods_details_footer_model.dart ファイルの表示

@@ -2,6 +2,7 @@ class GoodsDetailsFooterModel {
String collect;
String collect_color;
String collect_icon;
String collected_icon;
String home;
String home_color;
String home_icon;
@@ -18,28 +19,46 @@ class GoodsDetailsFooterModel {
String share_value;
String self_buy_value;

GoodsDetailsFooterModel(
{this.collect,
this.collect_color,
this.collect_icon,
this.home,
this.home_color,
this.home_icon,
this.save_earn,
this.save_earn_bg1_color,
this.save_earn_bg2_color,
this.save_earn_color,
this.save_earn_val_color,
this.share_earn,
this.share_earn_bg1_color,
this.share_earn_bg2_color,
this.share_earn_color,
this.share_earn_val_color,
this.share_value,
this.self_buy_value});
String isFav;
String buyUrl;
String shareUrl;
FavArgs favArgs;
ShareUrlArgs shareUrlArgs;

GoodsDetailsFooterModel({
this.buyUrl,
this.favArgs,
this.isFav,
this.shareUrl,
this.shareUrlArgs,
this.collect,
this.collect_color,
this.collect_icon,
this.home,
this.home_color,
this.home_icon,
this.save_earn,
this.save_earn_bg1_color,
this.save_earn_bg2_color,
this.save_earn_color,
this.save_earn_val_color,
this.share_earn,
this.share_earn_bg1_color,
this.share_earn_bg2_color,
this.share_earn_color,
this.share_earn_val_color,
this.share_value,
this.self_buy_value,
this.collected_icon,
});

factory GoodsDetailsFooterModel.fromJson(Map<String, dynamic> json) {
return GoodsDetailsFooterModel(
isFav: json['is_fav'],
buyUrl: json['buy_url'],
shareUrl: json['share_url'],
favArgs: json['fav_args'] != null ? new FavArgs.fromJson(json['fav_args']) : null,
shareUrlArgs: json['share_url_args'] != null ? new ShareUrlArgs.fromJson(json['share_url_args']) : null,
collect: json['collect'],
collect_color: json['collect_color'],
collect_icon: json['collect_icon'],
@@ -58,6 +77,7 @@ class GoodsDetailsFooterModel {
share_earn_val_color: json['share_earn_val_color'],
share_value: json['share_value'],
self_buy_value: json['self_buy_value'],
collected_icon: json['collected_icon'],
);
}

@@ -81,6 +101,120 @@ class GoodsDetailsFooterModel {
data['share_earn_val_color'] = this.share_earn_val_color;
data['share_value'] = this.share_value;
data['self_buy_value'] = this.self_buy_value;
data['collected_icon'] = this.collected_icon;

data['is_fav'] = this.isFav;
data['buy_url'] = this.buyUrl;
data['share_url'] = this.shareUrl;
if (this.favArgs != null) {
data['fav_args'] = this.favArgs.toJson();
}
if (this.shareUrlArgs != null) {
data['share_url_args'] = this.shareUrlArgs.toJson();
}

return data;
}
}

class FavArgs {
String provider;
String providerName;
String goodId;
String goodImage;
String goodTitle;
String shopName;
String coupon;
String couponUrl;
String commission;
String marketPrice;
String currentPrice;
String inorderCount;
Null detailData;

FavArgs(
{this.provider,
this.providerName,
this.goodId,
this.goodImage,
this.goodTitle,
this.shopName,
this.coupon,
this.couponUrl,
this.commission,
this.marketPrice,
this.currentPrice,
this.inorderCount,
this.detailData});

FavArgs.fromJson(Map<String, dynamic> json) {
provider = json['provider'];
providerName = json['provider_name'];
goodId = json['good_id'];
goodImage = json['good_image'];
goodTitle = json['good_title'];
shopName = json['shop_name'];
coupon = json['coupon'];
couponUrl = json['coupon_url'];
commission = json['commission'];
marketPrice = json['market_price'];
currentPrice = json['current_price'];
inorderCount = json['inorder_count'];
detailData = json['detail_data'];
}

Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['provider'] = this.provider;
data['provider_name'] = this.providerName;
data['good_id'] = this.goodId;
data['good_image'] = this.goodImage;
data['good_title'] = this.goodTitle;
data['shop_name'] = this.shopName;
data['coupon'] = this.coupon;
data['coupon_url'] = this.couponUrl;
data['commission'] = this.commission;
data['market_price'] = this.marketPrice;
data['current_price'] = this.currentPrice;
data['inorder_count'] = this.inorderCount;
data['detail_data'] = this.detailData;
return data;
}
}

class ShareUrlArgs {
String buyUrl;
String coupon;
String couponPrice;
String marketPrice;
String posterImage;
String selfbuyCommission;
String title;
String type;

ShareUrlArgs({this.buyUrl, this.coupon, this.couponPrice, this.marketPrice, this.posterImage, this.selfbuyCommission, this.title, this.type});

ShareUrlArgs.fromJson(Map<String, dynamic> json) {
buyUrl = json['buy_url'];
coupon = json['coupon'];
couponPrice = json['coupon_price'];
marketPrice = json['market_price'];
posterImage = json['poster_image'];
selfbuyCommission = json['selfbuy_commission'];
title = json['title'];
type = json['type'];
}

Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['buy_url'] = this.buyUrl;
data['coupon'] = this.coupon;
data['coupon_price'] = this.couponPrice;
data['market_price'] = this.marketPrice;
data['poster_image'] = this.posterImage;
data['selfbuy_commission'] = this.selfbuyCommission;
data['title'] = this.title;
data['type'] = this.type;
return data;
}
}

+ 6
- 2
lib/widgets/home/home_goods/home_goods_item_single.dart ファイルの表示

@@ -14,8 +14,12 @@ class HomeGoodsItemSingle extends StatelessWidget {
final HomeGoodsModel goods;
final HomeGoodsStyleModel style;
Map<String, dynamic> data;
final double marginLeft;
final double marginRight;
final double marginTop;
final double marginBottom;

HomeGoodsItemSingle(this.goods, this.style, {Key key, this.data})
HomeGoodsItemSingle(this.goods, this.style, {Key key, this.data, this.marginBottom = 4, this.marginTop = 4, this.marginLeft= 12.5, this.marginRight = 12.5})
: super(key: key) {
if (this.data != null && this.data.containsKey('data')) {
String data = this.data['data'];
@@ -36,7 +40,7 @@ class HomeGoodsItemSingle extends StatelessWidget {
return GestureDetector(
onTap: () => _onJumpGoodsDetails(context, goods),
child: Container(
margin: EdgeInsets.only(left: 12.5, right: 12.5, top: 4, bottom: 4),
margin: EdgeInsets.only(left: marginLeft, right: marginRight, top: marginTop, bottom: marginBottom),
padding: EdgeInsets.all(7.5),
decoration: BoxDecoration(
color: Colors.white,


+ 15
- 0
lib/widgets/home/home_goods/models/home_goods_model.dart ファイルの表示

@@ -44,6 +44,21 @@ class HomeGoodsModel extends Equatable {
detailData = json['detail_data'];
}

void fromJson(Map<String, dynamic> json) {
provider = json['provider'];
providerName = json['provider_name'];
goodId = json['good_id'];
goodImage = json['good_image'];
goodTitle = json['good_title'];
shopName = json['shop_name'];
coupon = json['coupon'];
commission = json['commission'];
marketPrice = json['market_price'];
currentPrice = json['current_price'];
inorderCount = json['inorder_count'];
detailData = json['detail_data'];
}

Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['provider'] = this.provider;


読み込み中…
キャンセル
保存