Commit 2879454a478c847cbaf6267adda145a29b1d48ac

Authored by 吴启风
1 parent a4c3106a

feat:调通支付宝支付&游戏列表页

assets/images/bg_frame_module_pic.png renamed to assets/images/bg_frame_module.png

54.1 KB

lib/common/request/apis.dart
... ... @@ -85,4 +85,11 @@ class Apis {
85 85  
86 86 /// 商品列表
87 87 static const String productList = 'order/course/combo/list';
  88 +
  89 + /// 创建订单
  90 + static const String createOrder = 'order/create/order';
  91 +
  92 + /// 获取阿里支付token
  93 + static const String getAliPayToken = 'pay/alipay/token';
  94 +
88 95 }
... ...
lib/common/request/dao/shop_dao.dart 0 → 100644
  1 +import '../../../models/product_entity.dart';
  2 +import '../request_client.dart';
  3 +
  4 +class ShopDao {
  5 + ///商品列表
  6 + static Future productList() async {
  7 + return await requestClient.get<List<ProductEntity?>>(Apis.productList);
  8 + }
  9 +
  10 + ///创建订单
  11 + static Future createOrder(ProductEntity productEntity) async {
  12 + return await requestClient
  13 + .post<Map<String, dynamic>>(Apis.createOrder, data: {'courseComboId': productEntity.id});
  14 + }
  15 +
  16 + ///获取ali支付订单信息
  17 + static Future getAliPayToken(String orderNo) async {
  18 + return await requestClient
  19 + .post<Map<String, dynamic>>(Apis.getAliPayToken, data: {'orderNo': orderNo});
  20 + }
  21 +}
... ...
lib/pages/games/bloc.dart
1 1 import 'package:bloc/bloc.dart';
2 2 import 'package:flutter/cupertino.dart';
3 3 import 'package:flutter/services.dart';
  4 +import 'package:wow_english/common/extension/string_extension.dart';
4 5  
5 6 import 'event.dart';
  7 +import 'game_entity.dart';
6 8 import 'state.dart';
7 9  
8 10 class GamesBloc extends Bloc<GamesEvent, GamesState> {
9 11  
10 12 late MethodChannel _methodChannel;
11 13  
  14 + //手动初始化4个GameEntity对象
  15 + final List<GameEntity> _games = [
  16 + GameEntity()
  17 + ..id = 1
  18 + ..imageName = 'pic_module_game'.assetPng
  19 + ..name = '游戏1',
  20 + GameEntity()
  21 + ..id = 2
  22 + ..imageName = 'pic_module_game'.assetPng
  23 + ..name = '游戏2',
  24 + GameEntity()
  25 + ..id = 3
  26 + ..imageName = 'pic_module_game'.assetPng
  27 + ..name = '游戏3',
  28 + GameEntity()
  29 + ..id = 4
  30 + ..imageName = 'pic_module_game'.assetPng
  31 + ..name = '游戏4'
  32 + ];
  33 +
  34 + List<GameEntity> get listData => _games;
  35 +
12 36 GamesBloc() : super(GamesState().init()) {
13 37 on<InitEvent>(_init);
14 38 on<GotoGamePageEvent>(_gotoGamePage);
... ...
lib/pages/games/game_entity.dart 0 → 100644
  1 +
  2 +
  3 +
  4 +class GameEntity {
  5 +
  6 + late int id;
  7 +
  8 + late String imageName;
  9 +
  10 + late String name;
  11 +}
0 12 \ No newline at end of file
... ...
lib/pages/games/view.dart
... ... @@ -2,7 +2,6 @@ import &#39;package:flutter/material.dart&#39;;
2 2 import 'package:flutter_bloc/flutter_bloc.dart';
3 3  
4 4 import 'package:flutter_screenutil/flutter_screenutil.dart';
5   -import 'package:wow_english/common/extension/string_extension.dart';
6 5 import 'package:wow_english/common/widgets/we_app_bar.dart';
7 6 import 'package:wow_english/pages/games/state.dart';
8 7  
... ... @@ -26,12 +25,10 @@ class _GamesPageView extends StatelessWidget {
26 25 @override
27 26 Widget build(BuildContext context) {
28 27 return BlocListener<GamesBloc, GamesState>(
29   - listener: (context, state) {
30   -
31   - },
  28 + listener: (context, state) {},
32 29 child: Scaffold(
33 30 appBar: const WEAppBar(
34   - titleText: '游戏列表',
  31 + titleText: '游戏专区',
35 32 centerTitle: false,
36 33 ),
37 34 body: _gamesView(),
... ... @@ -39,41 +36,39 @@ class _GamesPageView extends StatelessWidget {
39 36 );
40 37 }
41 38  
42   - Widget _gamesView() => BlocBuilder<GamesBloc, GamesState>(
43   - builder: (context, state) {
  39 + Widget _gamesView() =>
  40 + BlocBuilder<GamesBloc, GamesState>(builder: (context, state) {
44 41 final bloc = BlocProvider.of<GamesBloc>(context);
45 42 //屏幕中间横着放四张图片一行展示(尺寸120*200),每张图片下方有行文字
46   - return GridView.builder(
47   - gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
48   - crossAxisCount: 4,
49   - crossAxisSpacing: 10,
50   - mainAxisSpacing: 10,
51   - childAspectRatio: 0.6,
52   - ),
53   - itemCount: 4,
54   - itemBuilder: (BuildContext context, int index) {
55   - // final entity = bloc.listData[index];
56   - return GestureDetector(
57   - onTap: () {
58   - bloc.add(GotoGamePageEvent(1));
59   - },
60   - child: Container(
61   - decoration: BoxDecoration(
62   - border: Border.all(
63   - width: 1.0,
64   - color: const Color(0xFF140C10),
65   - ),
66   - borderRadius: BorderRadius.circular(21),
67   - ),
68   - child: Column(
69   - children: [
70   - Image.asset('pic_module_study'.assetPng, width: 120, height: 200),
71   - Text('游戏名称', style: TextStyle(fontSize: 14.sp, color: const Color(0xFF140C10)))
72   - ],
73   - ),
  43 + return Container(
  44 + margin: EdgeInsets.symmetric(horizontal: 50.0.w),
  45 + child: GridView.builder(
  46 + padding: EdgeInsets.zero,
  47 + // physics: const NeverScrollableScrollPhysics(),
  48 + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
  49 + crossAxisCount: 3,
  50 + crossAxisSpacing: 10,
  51 + mainAxisSpacing: 10,
  52 + childAspectRatio: 0.6,
74 53 ),
75   - );
  54 + itemCount: bloc.listData.length,
  55 + itemBuilder: (BuildContext context, int index) {
  56 + final gameEntity = bloc.listData[index];
  57 + return GestureDetector(
  58 + onTap: () {
  59 + bloc.add(GotoGamePageEvent(gameEntity.id));
  60 + },
  61 + child: Column(
  62 + children: [
  63 + Image.asset(gameEntity.imageName,
  64 + width: 120, height: 200),
  65 + Text(gameEntity.name,
  66 + style: TextStyle(
  67 + fontSize: 14.sp,
  68 + color: const Color(0xFF140C10)))
  69 + ],
  70 + ),
  71 + );
  72 + }));
76 73 });
77   - });
78 74 }
79   -
... ...
lib/pages/moduleSelect/view.dart
... ... @@ -52,8 +52,18 @@ class _HomePageView extends StatelessWidget {
52 52 child: Column(
53 53 mainAxisAlignment: MainAxisAlignment.center,
54 54 children: [
55   - Image.asset('pic_module_study'.assetPng,
56   - width: 162.5.w, height: 203.5.h),
  55 + Stack(
  56 + alignment: AlignmentDirectional.center,
  57 + children: [
  58 + Image.asset('bg_frame_module'.assetPng,
  59 + width: 162.5.w, height: 203.5.h),
  60 + Center(
  61 + child: Image.asset(
  62 + 'pic_module_study'.assetPng,
  63 + width: 140.5.w,
  64 + height: 172.h),
  65 + )
  66 + ]),
57 67 10.verticalSpace,
58 68 Image.asset('label_module_study'.assetPng,
59 69 width: 124.w, height: 34.h),
... ... @@ -69,8 +79,14 @@ class _HomePageView extends StatelessWidget {
69 79 child: Column(
70 80 mainAxisAlignment: MainAxisAlignment.center,
71 81 children: [
72   - Image.asset('pic_module_game'.assetPng,
73   - width: 162.5.w, height: 203.5.h),
  82 + Stack(
  83 + alignment: AlignmentDirectional.center,
  84 + children: [
  85 + Image.asset('bg_frame_module'.assetPng,
  86 + width: 162.5.w, height: 203.5.h),
  87 + Image.asset('pic_module_game'.assetPng,
  88 + width: 140.5.w, height: 172.h)
  89 + ]),
74 90 10.verticalSpace,
75 91 Image.asset('label_module_game'.assetPng,
76 92 width: 124.w, height: 34.h),
... ...
lib/pages/shop/home/widgets/product_item.dart
... ... @@ -31,37 +31,36 @@ class ProductItem extends StatelessWidget {
31 31 child: Row(
32 32 mainAxisAlignment: MainAxisAlignment.spaceBetween,
33 33 children: [
34   - // Container(
35   - // width: 124.w,
36   - // decoration: BoxDecoration(
37   - // border: Border.all(
38   - // width: 1.0,
39   - // color: const Color(0xFF333333),
40   - // ),
41   - // image: const DecorationImage(
42   - //
43   - // image: NetworkImage(entity?.coverUrl??''),
44   - // )
45   - // ),
46   - // ),
47   - CachedNetworkImage(
48   - imageUrl: entity?.picUrl ?? '',
49   - fit: BoxFit.fill,
50   - imageBuilder: (context, imageProvider) =>
51   - Container(
52   - decoration: BoxDecoration(
53   - border: Border.all(
54   - width: 1.0,
55   - color: const Color(0xFF333333),
56   - ),
57   - borderRadius: BorderRadius.circular(5.0),
58   - ),
59   - ),
60   - placeholder: (context, url) => CircularProgressIndicator(),
61   - // errorWidget: (context, url, error) => const Icon(Icons.error),
62   - height: 124.h,
  34 + Container(
63 35 width: 124.w,
  36 + decoration: BoxDecoration(
  37 + border: Border.all(
  38 + width: 1.0,
  39 + color: const Color(0xFF333333),
  40 + ),
  41 + image: DecorationImage(
  42 + image: NetworkImage(entity?.picUrl ?? ''),
  43 + )
  44 + ),
64 45 ),
  46 + // CachedNetworkImage(
  47 + // imageUrl: entity?.picUrl ?? '',
  48 + // fit: BoxFit.fill,
  49 + // imageBuilder: (context, imageProvider) =>
  50 + // Container(
  51 + // decoration: BoxDecoration(
  52 + // border: Border.all(
  53 + // width: 1.0,
  54 + // color: const Color(0xFF333333),
  55 + // ),
  56 + // borderRadius: BorderRadius.circular(5.0),
  57 + // ),
  58 + // ),
  59 + // placeholder: (context, url) => const CircularProgressIndicator(),
  60 + // // errorWidget: (context, url, error) => const Icon(Icons.error),
  61 + // height: 124.h,
  62 + // width: 124.w,
  63 + // ),
65 64 21.5.horizontalSpace,
66 65 Expanded(
67 66 child: Column(
... ...
lib/pages/shopping/bloc.dart
1 1 import 'package:bloc/bloc.dart';
  2 +import 'package:fluwx/fluwx.dart';
  3 +import 'package:tobias/tobias.dart';
  4 +import 'package:wow_english/generated/json/base/json_convert_content.dart';
2 5 import 'package:wow_english/models/product_entity.dart';
3 6  
  7 +import '../../common/request/dao/shop_dao.dart';
  8 +import '../../common/request/exception.dart';
  9 +import '../../utils/loading.dart';
4 10 import '../../utils/log_util.dart';
  11 +import '../../utils/toast_util.dart';
5 12 import 'event.dart';
6 13 import 'state.dart';
7 14  
... ... @@ -20,6 +27,7 @@ class ShoppingBloc extends Bloc&lt;ShoppingEvent, ShoppingState&gt; {
20 27 //页面初始化时刻
21 28 on<InitEvent>(_init);
22 29 on<ChangePaymentChannelEvent>(_changePaymentChannel);
  30 + on<DoPayEvent>(_startPay);
23 31 }
24 32  
25 33 void _init(InitEvent event, Emitter<ShoppingState> emit) async {
... ... @@ -37,8 +45,67 @@ class ShoppingBloc extends Bloc&lt;ShoppingEvent, ShoppingState&gt; {
37 45 }
38 46 }
39 47  
40   - void _gotoPay() {
41   - // 跳转到支付页面
  48 + void _startPay(DoPayEvent event,
  49 + Emitter<ShoppingState> emitter) async {
  50 + Log.d("开始支付 ${event.productEntity} ${event.paymentChannel}");
  51 + //如果event.productEntity为空,中断流程并toast提示
  52 + if (event.productEntity == null) {
  53 + showToast("商品信息为空");
  54 + return;
  55 + }
  56 + final productEntitySafely = event.productEntity!;
  57 + try {
  58 + await loading(() async {
  59 + final Map<String, dynamic> orderInfo = await ShopDao.createOrder(productEntitySafely);
  60 + Log.d("orderInfo $orderInfo");
  61 + final String? orderNo = orderInfo.getOrNull("orderNo");
  62 + if (orderNo == null) {
  63 + showToast("订单创建失败");
  64 + return;
  65 + }
  66 + Log.d("orderNo $orderNo");
  67 +
  68 + if (event.paymentChannel == PaymentChannel.wechatPay) {
  69 + Fluwx fluwx = Fluwx();
  70 + fluwx.registerApi(appId: "wxd930ea5d5a228f5f",
  71 + universalLink: "https://app-api.wowenglish.com.cn/.well-known/apple-app-site-association");
  72 + // fluwx.pay(
  73 + // which: Payment(
  74 + // appId: _orderInfo['appid'].toString(),
  75 + // partnerId: _orderInfo['partnerid'].toString(),
  76 + // prepayId: _orderInfo['prepayid'].toString(),
  77 + // packageValue: _orderInfo['package'].toString(),
  78 + // nonceStr: _orderInfo['noncestr'].toString(),
  79 + // timestamp: _orderInfo['timestamp'],
  80 + // sign: _orderInfo['sign'].toString(),
  81 + // ));
  82 + } else {
  83 + final Map<String, dynamic> aliPayOrderInfo = await ShopDao.getAliPayToken(orderNo);
  84 + Log.d("aliPayOrderInfo=$aliPayOrderInfo type=${aliPayOrderInfo.runtimeType}");
  85 + final String? aliPayInfo = aliPayOrderInfo.getOrNull("token");
  86 + if (aliPayInfo == null) {
  87 + showToast("支付宝订单创建失败");
  88 + return;
  89 + }
  90 + Log.d("aliPayInfo=$aliPayInfo");
  91 + ///打印aliPayOrderInfo的type
  92 + Tobias tobias = Tobias();
  93 + final Map aliPayResult = await tobias.pay(aliPayInfo);
  94 + Log.d("aliPayResult=$aliPayResult");
  95 + // 判断resultStatus 为9000则代表支付成功
  96 + if (aliPayResult.getOrNull("resultStatus") == "9000") {
  97 + showToast("支付成功");
  98 + emit(PaySuccessState());
  99 + } else {
  100 + showToast("支付失败");
  101 + }
  102 + }
  103 + });
  104 + } catch (e) {
  105 + if (e is ApiException) {
  106 + showToast(e.message ?? '请求失败,请检查网络连接');
  107 + }
  108 + }
42 109 }
43 110 }
44 111  
... ...
lib/pages/shopping/event.dart
  1 +import 'package:wow_english/models/product_entity.dart';
  2 +
1 3 import 'bloc.dart';
2 4  
3 5 abstract class ShoppingEvent {}
... ... @@ -8,4 +10,13 @@ class ChangePaymentChannelEvent extends ShoppingEvent {
8 10 final PaymentChannel paymentChannel;
9 11  
10 12 ChangePaymentChannelEvent(this.paymentChannel);
  13 +}
  14 +
  15 +class DoPayEvent extends ShoppingEvent {
  16 +
  17 + final ProductEntity? productEntity;
  18 +
  19 + final PaymentChannel paymentChannel;
  20 +
  21 + DoPayEvent(this.productEntity, this.paymentChannel);
11 22 }
12 23 \ No newline at end of file
... ...
lib/pages/shopping/state.dart
... ... @@ -8,4 +8,6 @@ class ShoppingState {
8 8 }
9 9 }
10 10  
11   -class PaymentChannelChangeState extends ShoppingState {}
12 11 \ No newline at end of file
  12 +class PaymentChannelChangeState extends ShoppingState {}
  13 +
  14 +class PaySuccessState extends ShoppingState {}
13 15 \ No newline at end of file
... ...
lib/pages/shopping/view.dart
... ... @@ -8,6 +8,7 @@ import &#39;package:wow_english/models/product_entity.dart&#39;;
8 8 import '../../common/core/assets_const.dart';
9 9 import '../../common/widgets/we_app_bar.dart';
10 10 import '../../utils/image_util.dart';
  11 +import '../../utils/log_util.dart';
11 12 import 'bloc.dart';
12 13 import 'event.dart';
13 14 import 'state.dart';
... ... @@ -66,13 +67,18 @@ class _ShoppingView extends StatelessWidget {
66 67 @override
67 68 Widget build(BuildContext context) {
68 69 final bloc = BlocProvider.of<ShoppingBloc>(context);
  70 + // var title1 = bloc.productData?.name ?? '';
69 71 return BlocListener<ShoppingBloc, ShoppingState>(
70 72 listener: (context, state) {
  73 + Log.d("wqf state=$state");
  74 + if (state is PaySuccessState) {
  75 + Navigator.pop(context);
  76 + }
71 77 },
72 78 child: Scaffold(
73 79 appBar: const WEAppBar(
74 80 //标题传进来的
75   - titleText: '支付',
  81 + titleText: "商品详情",
76 82 ),
77 83 body: Container(
78 84 margin: const EdgeInsets.only(left: 80.0, top: 28.0, right: 56.0),
... ... @@ -80,7 +86,7 @@ class _ShoppingView extends StatelessWidget {
80 86 crossAxisAlignment: CrossAxisAlignment.start,
81 87 children: [
82 88 CachedNetworkImage(
83   - imageUrl: "${bloc.productData?.detailPicUrl}",
  89 + imageUrl: bloc.productData?.detailPicUrl ?? '',
84 90 imageBuilder: (context, imageProvider) =>
85 91 Container(
86 92 decoration: BoxDecoration(
... ... @@ -91,8 +97,7 @@ class _ShoppingView extends StatelessWidget {
91 97 borderRadius: BorderRadius.circular(5.0),
92 98 ),
93 99 ),
94   - placeholder: (context, url) =>
95   - const CircularProgressIndicator(),
  100 + placeholder: (context, url) => const CircularProgressIndicator(),
96 101 // errorWidget: (context, url, error) => const Icon(Icons.error),
97 102 height: 210.0.h,
98 103 width: 210.0.w,
... ... @@ -153,14 +158,15 @@ Widget _paymentWidget() =&gt;
153 158 text: PaymentChannel.aliPay.payChannelName,
154 159 groupValue: bloc.curPaymentChannel.payChannelType,
155 160 onChanged: (newValue) {
156   - bloc.add(
157   - ChangePaymentChannelEvent(PaymentChannel.aliPay));
  161 + bloc.add(ChangePaymentChannelEvent(PaymentChannel.aliPay));
158 162 },
159 163 ),
160 164 const SizedBox(height: 15.0),
161 165 // 确认支付按钮
162 166 InkWell(
163 167 onTap: () {
  168 + Log.d('点击支付按钮 ${bloc.productData}');
  169 + bloc.add(DoPayEvent(bloc.productData, bloc.curPaymentChannel));
164 170 },
165 171 child: Image(
166 172 width: 125.w,
... ...
pubspec.yaml
... ... @@ -66,7 +66,7 @@ dependencies:
66 66 # 拍照,从相册中选择 https://pub.flutter-io.cn/packages/image_picker
67 67 image_picker: ^0.8.7+5
68 68 # 支付宝支付SDK https://pub.flutter-io.cn/packages/tobias
69   - tobias: ^3.1.0
  69 + tobias: ^3.3.2
70 70 # 微信SDK相关 https://pub.flutter-io.cn/packages/fluwx
71 71 fluwx: ^4.5.5
72 72 # json数据解析 https://pub.flutter-io.cn/packages/json_annotation
... ... @@ -74,7 +74,9 @@ dependencies:
74 74 # double丢失精度问题 https://pub.dev/packages/decimal
75 75 decimal: ^2.3.2
76 76 # 网络图片缓存 https://pub.flutter-io.cn/packages/cached_network_image
77   - cached_network_image: ^3.2.3
  77 + cached_network_image: ^3.3.1
  78 +# # 网络图片缓存 https://pub.dev/packages/extended_image
  79 +# extended_image: ^4.0.0
78 80 # 常用工具类(时间轴,倒计时等) https://pub.flutter-io.cn/packages/common_utils
79 81 common_utils: ^2.1.0
80 82 # 获取设备信息 https://pub.flutter-io.cn/packages/device_info_plus
... ...