Commit 075991052ccffde24b784e5c2b48abeecc4cf261
1 parent
4224b3f8
feat:商品列表请求&路由
Showing
12 changed files
with
155 additions
and
137 deletions
lib/common/request/apis.dart
lib/generated/json/product_entity.g.dart
... | ... | @@ -3,31 +3,96 @@ import 'package:wow_english/models/product_entity.dart'; |
3 | 3 | |
4 | 4 | ProductEntity $ProductEntityFromJson(Map<String, dynamic> json) { |
5 | 5 | final ProductEntity productEntity = ProductEntity(); |
6 | + final int? id = jsonConvert.convert<int>(json['id']); | |
7 | + if (id != null) { | |
8 | + productEntity.id = id; | |
9 | + } | |
6 | 10 | final String? name = jsonConvert.convert<String>(json['name']); |
7 | 11 | if (name != null) { |
8 | 12 | productEntity.name = name; |
9 | 13 | } |
14 | + final String? title = jsonConvert.convert<String>(json['title']); | |
15 | + if (title != null) { | |
16 | + productEntity.title = title; | |
17 | + } | |
10 | 18 | final double? price = jsonConvert.convert<double>(json['price']); |
11 | 19 | if (price != null) { |
12 | 20 | productEntity.price = price; |
13 | 21 | } |
22 | + final String? picUrl = jsonConvert.convert<String>(json['picUrl']); | |
23 | + if (picUrl != null) { | |
24 | + productEntity.picUrl = picUrl; | |
25 | + } | |
26 | + final String? bannerPicUrl = jsonConvert.convert<String>( | |
27 | + json['bannerPicUrl']); | |
28 | + if (bannerPicUrl != null) { | |
29 | + productEntity.bannerPicUrl = bannerPicUrl; | |
30 | + } | |
31 | + final String? detailPicUrl = jsonConvert.convert<String>( | |
32 | + json['detailPicUrl']); | |
33 | + if (detailPicUrl != null) { | |
34 | + productEntity.detailPicUrl = detailPicUrl; | |
35 | + } | |
36 | + final int? saleType = jsonConvert.convert<int>(json['saleType']); | |
37 | + if (saleType != null) { | |
38 | + productEntity.saleType = saleType; | |
39 | + } | |
40 | + final int? status = jsonConvert.convert<int>(json['status']); | |
41 | + if (status != null) { | |
42 | + productEntity.status = status; | |
43 | + } | |
44 | + final int? sortOrder = jsonConvert.convert<int>(json['sortOrder']); | |
45 | + if (sortOrder != null) { | |
46 | + productEntity.sortOrder = sortOrder; | |
47 | + } | |
48 | + final int? validityType = jsonConvert.convert<int>(json['validityType']); | |
49 | + if (validityType != null) { | |
50 | + productEntity.validityType = validityType; | |
51 | + } | |
14 | 52 | return productEntity; |
15 | 53 | } |
16 | 54 | |
17 | 55 | Map<String, dynamic> $ProductEntityToJson(ProductEntity entity) { |
18 | 56 | final Map<String, dynamic> data = <String, dynamic>{}; |
57 | + data['id'] = entity.id; | |
19 | 58 | data['name'] = entity.name; |
59 | + data['title'] = entity.title; | |
20 | 60 | data['price'] = entity.price; |
61 | + data['picUrl'] = entity.picUrl; | |
62 | + data['bannerPicUrl'] = entity.bannerPicUrl; | |
63 | + data['detailPicUrl'] = entity.detailPicUrl; | |
64 | + data['saleType'] = entity.saleType; | |
65 | + data['status'] = entity.status; | |
66 | + data['sortOrder'] = entity.sortOrder; | |
67 | + data['validityType'] = entity.validityType; | |
21 | 68 | return data; |
22 | 69 | } |
23 | 70 | |
24 | 71 | extension ProductEntityExtension on ProductEntity { |
25 | 72 | ProductEntity copyWith({ |
73 | + int? id, | |
26 | 74 | String? name, |
75 | + String? title, | |
27 | 76 | double? price, |
77 | + String? picUrl, | |
78 | + String? bannerPicUrl, | |
79 | + String? detailPicUrl, | |
80 | + int? saleType, | |
81 | + int? status, | |
82 | + int? sortOrder, | |
83 | + int? validityType, | |
28 | 84 | }) { |
29 | 85 | return ProductEntity() |
86 | + ..id = id ?? this.id | |
30 | 87 | ..name = name ?? this.name |
31 | - ..price = price ?? this.price; | |
88 | + ..title = title ?? this.title | |
89 | + ..price = price ?? this.price | |
90 | + ..picUrl = picUrl ?? this.picUrl | |
91 | + ..bannerPicUrl = bannerPicUrl ?? this.bannerPicUrl | |
92 | + ..detailPicUrl = detailPicUrl ?? this.detailPicUrl | |
93 | + ..saleType = saleType ?? this.saleType | |
94 | + ..status = status ?? this.status | |
95 | + ..sortOrder = sortOrder ?? this.sortOrder | |
96 | + ..validityType = validityType ?? this.validityType; | |
32 | 97 | } |
33 | 98 | } |
34 | 99 | \ No newline at end of file | ... | ... |
lib/models/product_entity.dart
... | ... | @@ -5,9 +5,30 @@ export 'package:wow_english/generated/json/product_entity.g.dart'; |
5 | 5 | |
6 | 6 | @JsonSerializable() |
7 | 7 | class ProductEntity { |
8 | - late String name; | |
9 | - late double price; | |
10 | - late String url; | |
8 | + | |
9 | + int? id; | |
10 | + | |
11 | + String? name; | |
12 | + | |
13 | + String? title; | |
14 | + | |
15 | + /// 售卖价格 | |
16 | + double? price; | |
17 | + /// 商品图片 | |
18 | + String? picUrl; | |
19 | + /// 商品banner图片 | |
20 | + String? bannerPicUrl; | |
21 | + /// 商品详情图片 | |
22 | + String? detailPicUrl; | |
23 | + /// 销售类型 | |
24 | + int? saleType; | |
25 | + /// 状态 | |
26 | + int? status; | |
27 | + /// 序号 | |
28 | + int? sortOrder; | |
29 | + /// 有效期类型 | |
30 | + int? validityType; | |
31 | + | |
11 | 32 | |
12 | 33 | ProductEntity(); |
13 | 34 | ... | ... |
lib/pages/home/widgets/home_tab_header_widget.dart
... | ... | @@ -104,14 +104,14 @@ class HomeTabHeaderWidget extends StatelessWidget { |
104 | 104 | } |
105 | 105 | }, |
106 | 106 | icon: Image.asset('listen'.assetPng)), |
107 | - // IconButton( | |
108 | - // onPressed: (){ | |
109 | - // if(actionTap != null) { | |
110 | - // actionTap!(HeaderActionType.shop); | |
111 | - // } | |
112 | - // }, | |
113 | - // icon: Image.asset('shop'.assetPng) | |
114 | - // ), | |
107 | + IconButton( | |
108 | + onPressed: (){ | |
109 | + if(actionTap != null) { | |
110 | + actionTap!(HeaderActionType.shop); | |
111 | + } | |
112 | + }, | |
113 | + icon: Image.asset('shop'.assetPng) | |
114 | + ), | |
115 | 115 | ScreenUtil().bottomBarHeight.horizontalSpace, |
116 | 116 | ], |
117 | 117 | ) | ... | ... |
lib/pages/shop/home/bloc/shop_home_bloc.dart
1 | 1 | |
2 | 2 | import 'package:bloc/bloc.dart'; |
3 | 3 | import 'package:meta/meta.dart'; |
4 | +import 'package:wow_english/common/request/dao/shop_dao.dart'; | |
5 | +import 'package:wow_english/models/product_entity.dart'; | |
6 | + | |
7 | +import '../../../../common/request/exception.dart'; | |
8 | +import '../../../../utils/loading.dart'; | |
9 | +import '../../../../utils/toast_util.dart'; | |
4 | 10 | |
5 | 11 | part 'shop_home_event.dart'; |
6 | 12 | part 'shop_home_state.dart'; |
7 | 13 | |
8 | 14 | class ShopHomeBloc extends Bloc<ShopHomeEvent, ShopHomeState> { |
15 | + | |
16 | + List<ProductEntity?> _productDatas = []; | |
17 | + | |
18 | + List<ProductEntity?> get productDatas => _productDatas; | |
19 | + | |
9 | 20 | ShopHomeBloc() : super(ShopHomeInitial()) { |
10 | 21 | on<ShopHomeEvent>((event, emit) { |
11 | 22 | // TODO: implement event handler |
12 | 23 | }); |
24 | + on<RequestDataEvent>(_requestData); | |
25 | + } | |
26 | + | |
27 | + void _requestData(RequestDataEvent event, Emitter<ShopHomeState> emitter) async { | |
28 | + try { | |
29 | + await loading(() async { | |
30 | + _productDatas = await ShopDao.productList() ?? []; | |
31 | + emitter(RequestListenDataState()); | |
32 | + }); | |
33 | + } catch (e) { | |
34 | + if (e is ApiException) { | |
35 | + showToast(e.message ?? '请求失败,请检查网络连接'); | |
36 | + } | |
37 | + } | |
13 | 38 | } |
14 | 39 | } | ... | ... |
lib/pages/shop/home/bloc/shop_home_event.dart
lib/pages/shop/home/bloc/shop_home_state.dart
lib/pages/shop/home/shop_home_page.dart
... | ... | @@ -3,7 +3,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; |
3 | 3 | import 'package:flutter_screenutil/flutter_screenutil.dart'; |
4 | 4 | import 'package:wow_english/common/extension/string_extension.dart'; |
5 | 5 | import 'package:wow_english/common/widgets/we_app_bar.dart'; |
6 | -import 'package:wow_english/pages/shop/home/widgets/lesson_card_item.dart'; | |
6 | +import 'package:wow_english/pages/shop/home/widgets/product_item.dart'; | |
7 | 7 | import 'package:wow_english/route/route.dart'; |
8 | 8 | import 'package:wow_english/utils/toast_util.dart'; |
9 | 9 | |
... | ... | @@ -15,7 +15,7 @@ class ShopHomePage extends StatelessWidget { |
15 | 15 | @override |
16 | 16 | Widget build(BuildContext context) { |
17 | 17 | return BlocProvider( |
18 | - create: (context) => ShopHomeBloc(), | |
18 | + create: (context) => ShopHomeBloc()..add(RequestDataEvent()), | |
19 | 19 | child: _ShopHomeView(), |
20 | 20 | ); |
21 | 21 | } |
... | ... | @@ -30,7 +30,9 @@ class _ShopHomeView extends StatelessWidget { |
30 | 30 | ); |
31 | 31 | } |
32 | 32 | |
33 | - Widget _shopHomeWidget() => BlocBuilder<ShopHomeBloc, ShopHomeState>(builder: (context, state) { | |
33 | + Widget _shopHomeWidget() => | |
34 | + BlocBuilder<ShopHomeBloc, ShopHomeState>(builder: (context, state) { | |
35 | + final bloc = BlocProvider.of<ShopHomeBloc>(context); | |
34 | 36 | return Scaffold( |
35 | 37 | appBar: WEAppBar( |
36 | 38 | actions: [ |
... | ... | @@ -62,7 +64,7 @@ class _ShopHomeView extends StatelessWidget { |
62 | 64 | child: Padding( |
63 | 65 | padding: EdgeInsets.symmetric(vertical: 25.h, horizontal: 25.w), |
64 | 66 | child: GridView.builder( |
65 | - itemCount: 4, | |
67 | + itemCount: bloc.productDatas.length, | |
66 | 68 | gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( |
67 | 69 | crossAxisCount: 2, |
68 | 70 | childAspectRatio: 2, |
... | ... | @@ -70,10 +72,10 @@ class _ShopHomeView extends StatelessWidget { |
70 | 72 | crossAxisSpacing: 4.5.w, |
71 | 73 | ), |
72 | 74 | itemBuilder: (BuildContext context, int index) { |
73 | - return LessonCardItem(onTap: () { | |
74 | - showToast('购买'); | |
75 | - pushNamed(AppRouteName.pay); | |
76 | - }); | |
75 | + final productEntity = bloc.productDatas[index]; | |
76 | + return ProductItem(onTap: () { | |
77 | + pushNamed(AppRouteName.pay, arguments: productEntity); | |
78 | + }, entity: productEntity); | |
77 | 79 | }), |
78 | 80 | ), |
79 | 81 | ), | ... | ... |
lib/pages/shop/home/widgets/lesson_card_item.dart deleted
1 | -import 'package:flutter/material.dart'; | |
2 | -import 'package:flutter_screenutil/flutter_screenutil.dart'; | |
3 | - | |
4 | -class LessonCardItem extends StatelessWidget { | |
5 | - const LessonCardItem({super.key, required this.onTap}); | |
6 | - | |
7 | - final Function() onTap; | |
8 | - | |
9 | - @override | |
10 | - Widget build(BuildContext context) { | |
11 | - return Container( | |
12 | - decoration: BoxDecoration( | |
13 | - borderRadius: BorderRadius.circular(10.r), | |
14 | - color: Colors.blue, | |
15 | - border: Border.all( | |
16 | - width: 1.0, | |
17 | - color: Colors.black | |
18 | - ) | |
19 | - // image: DecorationImage( | |
20 | - // image: AssetImage( | |
21 | - // ''.assetPng, | |
22 | - // ), | |
23 | - // fit: BoxFit.fill | |
24 | - // ) | |
25 | - ), | |
26 | - padding: EdgeInsets.symmetric(horizontal: 16.w,vertical: 16.h), | |
27 | - child: Row( | |
28 | - mainAxisAlignment: MainAxisAlignment.spaceBetween, | |
29 | - children: [ | |
30 | - Container( | |
31 | - width: 124.w, | |
32 | - decoration: BoxDecoration( | |
33 | - border: Border.all( | |
34 | - width: 1.0, | |
35 | - color: const Color(0xFF333333), | |
36 | - ), | |
37 | - image: const DecorationImage( | |
38 | - fit: BoxFit.fill, | |
39 | - image: NetworkImage('https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fsafe-img.xhscdn.com%2Fbw1%2Faa1c2213-820a-4223-8757-5f8cee318a28%3FimageView2%2F2%2Fw%2F1080%2Fformat%2Fjpg&refer=http%3A%2F%2Fsafe-img.xhscdn.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1688713226&t=192b18a613683bcdc5bd76f65c9ff032'), | |
40 | - ) | |
41 | - ), | |
42 | - ), | |
43 | - 21.5.horizontalSpace, | |
44 | - Expanded( | |
45 | - child: Column( | |
46 | - mainAxisAlignment: MainAxisAlignment.spaceBetween, | |
47 | - crossAxisAlignment: CrossAxisAlignment.end, | |
48 | - children: [ | |
49 | - Text( | |
50 | - 'Wow English 课程年卡', | |
51 | - softWrap: true, | |
52 | - textAlign: TextAlign.left, | |
53 | - style: TextStyle( | |
54 | - fontSize: 12.sp, | |
55 | - color: const Color(0xFF333333) | |
56 | - ), | |
57 | - ), | |
58 | - RichText( | |
59 | - text: TextSpan( | |
60 | - children:[ | |
61 | - TextSpan( | |
62 | - text: '¥', | |
63 | - style: TextStyle( | |
64 | - fontSize: 21.sp, | |
65 | - color: const Color(0xFFF51A1A), | |
66 | - ) | |
67 | - ), | |
68 | - TextSpan( | |
69 | - text: '998', | |
70 | - style: TextStyle( | |
71 | - fontSize: 40.sp, | |
72 | - color: const Color(0xFFF51A1A), | |
73 | - ), | |
74 | - ) | |
75 | - ] | |
76 | - ), | |
77 | - ), | |
78 | - GestureDetector( | |
79 | - onTap: () { | |
80 | - onTap(); | |
81 | - }, | |
82 | - child: Container( | |
83 | - decoration: BoxDecoration( | |
84 | - color: const Color(0xFFF5C51F), | |
85 | - borderRadius: BorderRadius.circular(5.r), | |
86 | - border: Border.all( | |
87 | - color: const Color(0xFF333333), | |
88 | - width: 1.0, | |
89 | - ) | |
90 | - ), | |
91 | - padding: EdgeInsets.symmetric( | |
92 | - vertical: 1.h, | |
93 | - horizontal: 26.5.w, | |
94 | - ), | |
95 | - child: Text( | |
96 | - '立即购买', | |
97 | - style: TextStyle( | |
98 | - fontSize: 10.sp, | |
99 | - color: const Color(0xFF333333) | |
100 | - ), | |
101 | - ), | |
102 | - ), | |
103 | - ) | |
104 | - ], | |
105 | - ), | |
106 | - ) | |
107 | - ], | |
108 | - ), | |
109 | - ); | |
110 | - } | |
111 | -} | |
112 | 0 | \ No newline at end of file |
lib/pages/shopping/bloc.dart
... | ... | @@ -16,7 +16,7 @@ class ShoppingBloc extends Bloc<ShoppingEvent, ShoppingState> { |
16 | 16 | PaymentChannel get curPaymentChannel => _curPaymentChannel; |
17 | 17 | |
18 | 18 | |
19 | - ShoppingBloc() : super(ShoppingState().init()) { | |
19 | + ShoppingBloc(this._productData) : super(ShoppingState().init()) { | |
20 | 20 | //页面初始化时刻 |
21 | 21 | on<InitEvent>(_init); |
22 | 22 | on<ChangePaymentChannelEvent>(_changePaymentChannel); | ... | ... |
lib/pages/shopping/view.dart
... | ... | @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; |
3 | 3 | import 'package:flutter_bloc/flutter_bloc.dart'; |
4 | 4 | import 'package:flutter_screenutil/flutter_screenutil.dart'; |
5 | 5 | import 'package:wow_english/common/extension/string_extension.dart'; |
6 | +import 'package:wow_english/models/product_entity.dart'; | |
6 | 7 | |
7 | 8 | import '../../common/core/assets_const.dart'; |
8 | 9 | import '../../common/widgets/we_app_bar.dart'; |
... | ... | @@ -45,13 +46,15 @@ Widget buildRadioOption({ |
45 | 46 | } |
46 | 47 | |
47 | 48 | class ShoppingPage extends StatelessWidget { |
48 | - const ShoppingPage({super.key}); | |
49 | + const ShoppingPage({super.key, this.productEntity}); | |
50 | + | |
51 | + final ProductEntity? productEntity; | |
49 | 52 | |
50 | 53 | @override |
51 | 54 | Widget build(BuildContext context) { |
52 | 55 | return BlocProvider( |
53 | 56 | create: (BuildContext context) => |
54 | - ShoppingBloc() | |
57 | + ShoppingBloc(productEntity) | |
55 | 58 | ..add(InitEvent()), |
56 | 59 | child: _ShoppingView(), |
57 | 60 | ); |
... | ... | @@ -77,7 +80,7 @@ class _ShoppingView extends StatelessWidget { |
77 | 80 | crossAxisAlignment: CrossAxisAlignment.start, |
78 | 81 | children: [ |
79 | 82 | CachedNetworkImage( |
80 | - imageUrl: "${bloc.productData?.url}", | |
83 | + imageUrl: "${bloc.productData?.detailPicUrl}", | |
81 | 84 | imageBuilder: (context, imageProvider) => |
82 | 85 | Container( |
83 | 86 | decoration: BoxDecoration( |
... | ... | @@ -111,7 +114,7 @@ Widget _paymentWidget() => |
111 | 114 | return Column( |
112 | 115 | crossAxisAlignment: CrossAxisAlignment.start, |
113 | 116 | children: [ |
114 | - Text('套餐价格:${bloc.productData?.price ?? "199(记得删除)"}', | |
117 | + Text('套餐价格:${bloc.productData?.price ?? ''}', | |
115 | 118 | style: TextStyle( |
116 | 119 | color: const Color(0xFF333333), fontSize: 16.sp)), |
117 | 120 | const SizedBox(height: 15.0), | ... | ... |
lib/route/route.dart
... | ... | @@ -2,6 +2,7 @@ import 'package:flutter/cupertino.dart'; |
2 | 2 | import 'package:flutter/material.dart'; |
3 | 3 | import 'package:wow_english/app/splash_page.dart'; |
4 | 4 | import 'package:wow_english/common/pages/wow_web_page.dart'; |
5 | +import 'package:wow_english/models/product_entity.dart'; | |
5 | 6 | import 'package:wow_english/pages/home/home_page.dart'; |
6 | 7 | import 'package:wow_english/pages/lessons/lesson_page.dart'; |
7 | 8 | import 'package:wow_english/pages/listen/listen_page.dart'; |
... | ... | @@ -26,6 +27,7 @@ import '../pages/reading/reading_page.dart'; |
26 | 27 | import '../pages/shopping/view.dart'; |
27 | 28 | import '../pages/user/setting/delete_account_page.dart'; |
28 | 29 | import '../pages/user/setting/reback_page.dart'; |
30 | +import '../utils/log_util.dart'; | |
29 | 31 | |
30 | 32 | class AppRouteName { |
31 | 33 | static const String splash = 'splash'; |
... | ... | @@ -104,7 +106,11 @@ class AppRouter { |
104 | 106 | case AppRouteName.shop: |
105 | 107 | return CupertinoPageRoute(builder: (_) => const ShopHomePage()); |
106 | 108 | case AppRouteName.pay: |
107 | - return CupertinoPageRoute(builder: (_) => const ShoppingPage()); | |
109 | + var productEntity = ProductEntity(); | |
110 | + if (settings.arguments != null && settings.arguments is ProductEntity) { | |
111 | + productEntity = settings.arguments as ProductEntity; | |
112 | + } | |
113 | + return CupertinoPageRoute(builder: (_) => ShoppingPage(productEntity: productEntity)); | |
108 | 114 | case AppRouteName.exLesson: |
109 | 115 | return CupertinoPageRoute(builder: (_) => const ExchangeLessonPage()); |
110 | 116 | case AppRouteName.exList: | ... | ... |