Commit d1d3222095c42820e46ac7d5e5105419e6a7eb89

Authored by liangchengyou
1 parent f0d56772

feat:兑换课程+购买记录

assets/images/bottom_grass.png 0 → 100644

342 KB

assets/images/check_lesson.png 0 → 100644

14.8 KB

assets/images/checked_logo.png 0 → 100644

10.4 KB

assets/images/ex_dis.png 0 → 100644

15.7 KB

assets/images/ex_sure.png 0 → 100644

17 KB

assets/images/listback.png 0 → 100644

50.1 KB

assets/images/wow_ex_lesson.png 0 → 100644

67.4 KB

lib/common/widgets/we_app_bar.dart
1 1 import 'package:flutter/material.dart';
2   -import 'package:flutter_screenutil/flutter_screenutil.dart';
3 2 import 'package:wow_english/common/extension/string_extension.dart';
4 3  
5 4 class WEAppBar extends StatelessWidget implements PreferredSizeWidget {
... ...
lib/home/home_page.dart
... ... @@ -31,7 +31,7 @@ class _HomePageView extends StatelessWidget {
31 31 } else if (type == HeaderActionType.listen) {
32 32 Navigator.of(AppRouter.context).pushNamed(AppRouteName.listen);
33 33 } else if (type == HeaderActionType.shop) {
34   -
  34 + Navigator.of(AppRouter.context).pushNamed(AppRouteName.shop);
35 35 } else {
36 36  
37 37 }
... ...
lib/lessons/lesson_page.dart
... ... @@ -12,7 +12,6 @@ class LessonPage extends StatelessWidget {
12 12  
13 13 final int? starPageIndex;
14 14  
15   -
16 15 @override
17 16 Widget build(BuildContext context) {
18 17 return BlocProvider(
... ...
lib/route/route.dart
... ... @@ -8,6 +8,9 @@ import 'package:wow_english/listen/listen_page.dart';
8 8 import 'package:wow_english/login/forgetpwd/forget_password_home_page.dart';
9 9 import 'package:wow_english/login/loginpage/login_page.dart';
10 10 import 'package:wow_english/login/setpwd/set_pwd_page.dart';
  11 +import 'package:wow_english/shop/exchane/exchange_lesson_page.dart';
  12 +import 'package:wow_english/shop/exchangelist/exchange_lesson_list_page.dart';
  13 +import 'package:wow_english/shop/home/shop_home_page.dart';
11 14 import 'package:wow_english/tab/tab_page.dart';
12 15  
13 16  
... ... @@ -20,6 +23,9 @@ class AppRouteName {
20 23 static const String webView = 'webView';
21 24 static const String lesson = 'lesson';
22 25 static const String listen = 'listen';
  26 + static const String shop = 'shop';
  27 + static const String exLesson = 'exLesson';
  28 + static const String exList = 'exList';
23 29 static const String tab = '/';
24 30 }
25 31  
... ... @@ -46,6 +52,12 @@ class AppRouter {
46 52 return CupertinoPageRoute(builder: (_) => const LessonPage());
47 53 case AppRouteName.listen:
48 54 return CupertinoPageRoute(builder: (_) => const ListenPage());
  55 + case AppRouteName.shop:
  56 + return CupertinoPageRoute(builder: (_) => const ShopHomePage());
  57 + case AppRouteName.exLesson:
  58 + return CupertinoPageRoute(builder: (_) => const ExchangeLessonPage());
  59 + case AppRouteName.exList:
  60 + return CupertinoPageRoute(builder: (_) => const ExchangeLessonListPage());
49 61 case AppRouteName.setPwd:
50 62 final phoneNum = (settings.arguments as Map)['phoneNumber'] as String;
51 63 return CupertinoPageRoute(builder: (_) => SetPassWordPage(phoneNum: phoneNum));
... ...
lib/shop/exchane/bloc/exchange_lesson_bloc.dart 0 → 100644
  1 +import 'package:flutter/cupertino.dart';
  2 +import 'package:flutter_bloc/flutter_bloc.dart';
  3 +
  4 +part 'exchange_lesson_event.dart';
  5 +part 'exchange_lesson_state.dart';
  6 +
  7 +class ExchangeLessonBloc extends Bloc<ExchangeLessonEvent, ExchangeLessonState> {
  8 +
  9 + final TextEditingController codeNumberController = TextEditingController();
  10 +
  11 + bool _checkCode = false;
  12 +
  13 + bool get checkCode => _checkCode;
  14 +
  15 + ExchangeLessonBloc() : super(ExchangeLessonInitial()) {
  16 + on<CodeNumberChangeEvent>(_codeNumberChange);
  17 + on<CheckCodeEvent>(_requestCheckCode);
  18 + }
  19 +
  20 + _codeNumberChange(CodeNumberChangeEvent event,Emitter<ExchangeLessonState> emitter) async {
  21 + if(codeNumberController.text.isNotEmpty) {
  22 + if (!_checkCode) {
  23 + _checkCode = true;
  24 + emitter(CheckCodeTypeChangeState());
  25 + }
  26 + } else {
  27 + if (_checkCode) {
  28 + _checkCode = false;
  29 + emitter(CheckCodeTypeChangeState());
  30 + }
  31 + }
  32 + }
  33 +
  34 + _requestCheckCode(CheckCodeEvent event, Emitter<ExchangeLessonState> emitter) async {
  35 + emitter(CheckCodeResultState(false));
  36 + }
  37 +}
... ...
lib/shop/exchane/bloc/exchange_lesson_event.dart 0 → 100644
  1 +part of 'exchange_lesson_bloc.dart';
  2 +
  3 +@immutable
  4 +abstract class ExchangeLessonEvent {}
  5 +//输入兑换码
  6 +class CodeNumberChangeEvent extends ExchangeLessonEvent {}
  7 +//验证兑换码
  8 +class CheckCodeEvent extends ExchangeLessonEvent {}
... ...
lib/shop/exchane/bloc/exchange_lesson_state.dart 0 → 100644
  1 +part of 'exchange_lesson_bloc.dart';
  2 +
  3 +@immutable
  4 +abstract class ExchangeLessonState {}
  5 +
  6 +class ExchangeLessonInitial extends ExchangeLessonState {}
  7 +//输入兑换码状态
  8 +class CheckCodeTypeChangeState extends ExchangeLessonState {}
  9 +//验证兑换码结果
  10 +class CheckCodeResultState extends ExchangeLessonState {
  11 + final bool result;
  12 + CheckCodeResultState(this.result);
  13 +}
... ...
lib/shop/exchane/exchange_lesson_page.dart 0 → 100644
  1 +import 'package:flutter/material.dart';
  2 +import 'package:flutter_bloc/flutter_bloc.dart';
  3 +import 'package:flutter_easyloading/flutter_easyloading.dart';
  4 +import 'package:flutter_screenutil/flutter_screenutil.dart';
  5 +import 'package:wow_english/common/extension/string_extension.dart';
  6 +import 'package:wow_english/common/widgets/textfield_customer_widget.dart';
  7 +import 'package:wow_english/route/route.dart';
  8 +
  9 +import 'bloc/exchange_lesson_bloc.dart';
  10 +
  11 +class ExchangeLessonPage extends StatelessWidget {
  12 + const ExchangeLessonPage({super.key});
  13 +
  14 + @override
  15 + Widget build(BuildContext context) {
  16 + return BlocProvider(
  17 + create: (context) => ExchangeLessonBloc(),
  18 + child: _ExchangeLessonPage(),
  19 + );
  20 + }
  21 +}
  22 +
  23 +class _ExchangeLessonPage extends StatelessWidget {
  24 + @override
  25 + Widget build(BuildContext context) {
  26 + return BlocListener<ExchangeLessonBloc,ExchangeLessonState>(
  27 + listener: (context, state){
  28 + if (state is CheckCodeResultState) {
  29 + String title = state.result?'兑换成功':'兑换失败';
  30 + EasyLoading.showToast(title);
  31 + Navigator.of(context).pushNamed(AppRouteName.exList);
  32 + }
  33 + },
  34 + child: _exchangeLessonPageView(),
  35 + );
  36 + }
  37 +
  38 + Widget _exchangeLessonPageView() => BlocBuilder<ExchangeLessonBloc,ExchangeLessonState>(
  39 + builder: (context, state){
  40 + final bloc = BlocProvider.of<ExchangeLessonBloc>(context);
  41 + return Scaffold(
  42 + resizeToAvoidBottomInset: false,
  43 + body: Container(
  44 + color: Colors.white,
  45 + child: Column(
  46 + mainAxisAlignment: MainAxisAlignment.center,
  47 + children: [
  48 + 10.5.verticalSpace,
  49 + Padding(
  50 + padding: EdgeInsets.symmetric(
  51 + horizontal: 15.w
  52 + ),
  53 + child: Row(
  54 + mainAxisAlignment: MainAxisAlignment.spaceBetween,
  55 + crossAxisAlignment: CrossAxisAlignment.start,
  56 + children: [
  57 + IconButton(
  58 + icon: Image.asset(
  59 + 'back_around'.assetPng,
  60 + width: 40,
  61 + height: 40,
  62 + ),
  63 + color: Colors.white,
  64 + onPressed: () {
  65 + Navigator.pop(context);
  66 + },
  67 + ),
  68 + Image.asset(
  69 + 'wow_ex_lesson'.assetPng,
  70 + width: 139.w,
  71 + height: 81.h,
  72 + ),
  73 + SizedBox.fromSize(
  74 + size: const Size(40.0, 40.0)
  75 + )
  76 + ],
  77 + ),
  78 + ),
  79 + Expanded(
  80 + child: Padding(
  81 + padding: EdgeInsets.symmetric(horizontal: 135.w),
  82 + child: Column(
  83 + mainAxisAlignment: MainAxisAlignment.center,
  84 + children: [
  85 + TextFieldCustomerWidget(
  86 + height: 55.h,
  87 + hitText: '请输入兑换码',
  88 + bgImageName: 'Input_layer_up',
  89 + textInputType: TextInputType.emailAddress,
  90 + controller: bloc.codeNumberController,
  91 + onChangeValue: (String value) {
  92 + bloc.add(CodeNumberChangeEvent());
  93 + },
  94 + ),
  95 + 21.5.verticalSpace,
  96 + GestureDetector(
  97 + onTap: () {
  98 + if (bloc.checkCode) {
  99 + bloc.add(CheckCodeEvent());
  100 + }
  101 + },
  102 + child: Container(
  103 + decoration: BoxDecoration(
  104 + image: DecorationImage(
  105 + image: AssetImage(
  106 + bloc.checkCode ? 'ex_sure'.assetPng:'ex_dis'.assetPng
  107 + ),
  108 + fit: BoxFit.fill
  109 + ),
  110 + ),
  111 + padding: EdgeInsets.symmetric(horizontal:27.w,vertical: 14.h),
  112 + child: Text(
  113 + '兑换',
  114 + style: TextStyle(
  115 + fontSize: 16.sp,
  116 + color: Colors.white
  117 + ),
  118 + ),
  119 + ),
  120 + )
  121 + ],
  122 + ),
  123 + ),
  124 + ),
  125 + Image.asset(
  126 + 'bottom_grass'.assetPng,
  127 + ),
  128 + ],
  129 + ),
  130 + ),
  131 + );
  132 + });
  133 +}
0 134 \ No newline at end of file
... ...
lib/shop/exchangelist/bloc/exchange_list_bloc.dart 0 → 100644
  1 +import 'dart:async';
  2 +
  3 +import 'package:bloc/bloc.dart';
  4 +import 'package:meta/meta.dart';
  5 +
  6 +part 'exchange_list_event.dart';
  7 +part 'exchange_list_state.dart';
  8 +
  9 +class ExchangeListBloc extends Bloc<ExchangeListEvent, ExchangeListState> {
  10 + ExchangeListBloc() : super(ExchangeListInitial()) {
  11 + on<ExchangeListEvent>((event, emit) {
  12 + // TODO: implement event handler
  13 + });
  14 + }
  15 +}
... ...
lib/shop/exchangelist/bloc/exchange_list_event.dart 0 → 100644
  1 +part of 'exchange_list_bloc.dart';
  2 +
  3 +@immutable
  4 +abstract class ExchangeListEvent {}
... ...
lib/shop/exchangelist/bloc/exchange_list_state.dart 0 → 100644
  1 +part of 'exchange_list_bloc.dart';
  2 +
  3 +@immutable
  4 +abstract class ExchangeListState {}
  5 +
  6 +class ExchangeListInitial extends ExchangeListState {}
... ...
lib/shop/exchangelist/exchange_lesson_list_page.dart 0 → 100644
  1 +import 'package:flutter/material.dart';
  2 +import 'package:flutter_bloc/flutter_bloc.dart';
  3 +import 'package:wow_english/common/widgets/we_app_bar.dart';
  4 +import 'package:wow_english/shop/exchangelist/widgets/exchange_list_item.dart';
  5 +
  6 +import 'bloc/exchange_list_bloc.dart';
  7 +
  8 +class ExchangeLessonListPage extends StatelessWidget {
  9 + const ExchangeLessonListPage({super.key});
  10 +
  11 + @override
  12 + Widget build(BuildContext context) {
  13 + return BlocProvider(
  14 + create: (context) => ExchangeListBloc(),
  15 + child: _ExchangeLessonListPageView(),
  16 + );
  17 + }
  18 +}
  19 +
  20 +class _ExchangeLessonListPageView extends StatelessWidget {
  21 + @override
  22 + Widget build(BuildContext context) {
  23 + return BlocListener<ExchangeListBloc,ExchangeListState>(
  24 + listener: (context,state){},
  25 + child: _exchangeLessonListView(),
  26 + );
  27 + }
  28 +
  29 + Widget _exchangeLessonListView() => BlocBuilder<ExchangeListBloc,ExchangeListState>(
  30 + builder: (context, state){
  31 + return Scaffold(
  32 + appBar: const WEAppBar(
  33 + titleText: '购买记录',
  34 + ),
  35 + body: SafeArea(
  36 + child: ListView.builder(
  37 + itemCount: 10,
  38 + itemBuilder: (BuildContext context,int index){
  39 + return ExchangeListItem(
  40 + isCheck: index%3==0,
  41 + );
  42 + }),
  43 + ),
  44 + );
  45 + }
  46 + );
  47 +}
0 48 \ No newline at end of file
... ...
lib/shop/exchangelist/widgets/exchange_list_item.dart 0 → 100644
  1 +import 'package:flutter/material.dart';
  2 +import 'package:flutter_screenutil/flutter_screenutil.dart';
  3 +import 'package:wow_english/common/extension/string_extension.dart';
  4 +
  5 +class ExchangeListItem extends StatelessWidget {
  6 + const ExchangeListItem({super.key, required this.isCheck});
  7 +
  8 + final bool isCheck;
  9 +
  10 + @override
  11 + Widget build(BuildContext context) {
  12 + return SizedBox(
  13 + height: 127.h,
  14 + width: double.infinity,
  15 + child: Padding(
  16 + padding: EdgeInsets.only(left: 32.w,right: 32.w,bottom:13.h),
  17 + child: GestureDetector(
  18 + onTap: () {
  19 +
  20 + },
  21 + child: Container(
  22 + decoration: BoxDecoration(
  23 + image: DecorationImage(
  24 + image: AssetImage(
  25 + 'listback'.assetPng,
  26 + ),
  27 + fit: BoxFit.fill
  28 + )
  29 + ),
  30 + padding: EdgeInsets.symmetric(horizontal: 12.w),
  31 + child: Row(
  32 + children: [
  33 + Container(
  34 + width: 70,
  35 + height: 70,
  36 + decoration: BoxDecoration(
  37 + borderRadius: BorderRadius.circular(5.r),
  38 + border: Border.all(
  39 + width: 1.0,
  40 + color: const Color(0xFF333333),
  41 + ),
  42 + image: const DecorationImage(
  43 + 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'),
  44 + ),
  45 + ),
  46 + ),
  47 + 17.horizontalSpace,
  48 + Expanded(
  49 + child: Column(
  50 + mainAxisAlignment: MainAxisAlignment.center,
  51 + children: [
  52 + Row(
  53 + children: [
  54 + Expanded(
  55 + child: Text(
  56 + '标题:wow english课程永久使用卡',
  57 + textAlign: TextAlign.left,
  58 + style: TextStyle(
  59 + fontSize: 20.sp,
  60 + color: const Color(0xFF333333)
  61 + ),
  62 + ),
  63 + ),
  64 + Offstage(
  65 + offstage: isCheck,
  66 + child: Image.asset(
  67 + 'checked_logo'.assetPng,
  68 + width: 33.w,
  69 + height: 35.h
  70 + ),
  71 + )
  72 + ],
  73 + ),
  74 + Padding(
  75 + padding: EdgeInsets.only(right: 35.w),
  76 + child: Row(
  77 + crossAxisAlignment: CrossAxisAlignment.end,
  78 + children: [
  79 + Expanded(
  80 + child: Text(
  81 + '兑换日期:2022-03-08 12:22:33',
  82 + textAlign: TextAlign.left,
  83 + style: TextStyle(
  84 + fontSize: 12.sp,
  85 + color: const Color(0xFF333333)
  86 + ),
  87 + ),
  88 + ),
  89 + Column(
  90 + mainAxisAlignment: MainAxisAlignment.end,
  91 + crossAxisAlignment: CrossAxisAlignment.start,
  92 + children: [
  93 + Text(
  94 + '生效日期:2023-01-03',
  95 + textAlign: TextAlign.left,
  96 + style: TextStyle(
  97 + fontSize: 12.sp,
  98 + color: const Color(0xFF333333)
  99 + ),
  100 + ),
  101 + Text(
  102 + '有效期至:永久有效',
  103 + textAlign: TextAlign.left,
  104 + style: TextStyle(
  105 + fontSize: 12.sp,
  106 + color: const Color(0xFF333333)
  107 + ),
  108 + )
  109 + ],
  110 + )
  111 + ],
  112 + ),
  113 + )
  114 + ],
  115 + ),
  116 + )
  117 + ],
  118 + ),
  119 + ),
  120 + ),
  121 + ),
  122 + );
  123 + }
  124 +}
0 125 \ No newline at end of file
... ...
lib/shop/home/bloc/shop_home_bloc.dart 0 → 100644
  1 +import 'dart:async';
  2 +
  3 +import 'package:bloc/bloc.dart';
  4 +import 'package:meta/meta.dart';
  5 +
  6 +part 'shop_home_event.dart';
  7 +part 'shop_home_state.dart';
  8 +
  9 +class ShopHomeBloc extends Bloc<ShopHomeEvent, ShopHomeState> {
  10 + ShopHomeBloc() : super(ShopHomeInitial()) {
  11 + on<ShopHomeEvent>((event, emit) {
  12 + // TODO: implement event handler
  13 + });
  14 + }
  15 +}
... ...
lib/shop/home/bloc/shop_home_event.dart 0 → 100644
  1 +part of 'shop_home_bloc.dart';
  2 +
  3 +@immutable
  4 +abstract class ShopHomeEvent {}
... ...
lib/shop/home/bloc/shop_home_state.dart 0 → 100644
  1 +part of 'shop_home_bloc.dart';
  2 +
  3 +@immutable
  4 +abstract class ShopHomeState {}
  5 +
  6 +class ShopHomeInitial extends ShopHomeState {}
... ...
lib/shop/home/shop_home_page.dart 0 → 100644
  1 +import 'package:flutter/material.dart';
  2 +import 'package:flutter_bloc/flutter_bloc.dart';
  3 +import 'package:flutter_easyloading/flutter_easyloading.dart';
  4 +import 'package:flutter_screenutil/flutter_screenutil.dart';
  5 +import 'package:wow_english/common/extension/string_extension.dart';
  6 +import 'package:wow_english/common/widgets/we_app_bar.dart';
  7 +import 'package:wow_english/route/route.dart';
  8 +import 'package:wow_english/shop/home/bloc/shop_home_bloc.dart';
  9 +
  10 +class ShopHomePage extends StatelessWidget {
  11 + const ShopHomePage({super.key});
  12 +
  13 + @override
  14 + Widget build(BuildContext context) {
  15 + return BlocProvider(
  16 + create: (context) => ShopHomeBloc(),
  17 + child: _ShopHomeView(),
  18 + );
  19 + }
  20 +}
  21 +
  22 +class _ShopHomeView extends StatelessWidget {
  23 + @override
  24 + Widget build(BuildContext context) {
  25 + return BlocListener<ShopHomeBloc,ShopHomeState>(
  26 + listener: (context, state) {},
  27 + child: _shopHomeWidget(),
  28 + );
  29 + }
  30 +
  31 + Widget _shopHomeWidget() => BlocBuilder<ShopHomeBloc,ShopHomeState>(builder: (context, state){
  32 + return Scaffold(
  33 + appBar: WEAppBar(
  34 + actions: [
  35 + IconButton(
  36 + icon: Image.asset(
  37 + 'check_lesson'.assetPng,
  38 + width: 40,
  39 + height: 40,
  40 + ),
  41 + color: Colors.white,
  42 + onPressed: () {
  43 + Navigator.of(context).pushNamed(AppRouteName.exLesson);
  44 + },
  45 + ),
  46 + IconButton(
  47 + icon: Image.asset(
  48 + 'shop'.assetPng,
  49 + width: 40,
  50 + height: 40,
  51 + ),
  52 + color: Colors.white,
  53 + onPressed: () {
  54 + EasyLoading.showToast('购前须知');
  55 + },
  56 + )
  57 + ],
  58 + ),
  59 + );
  60 + });
  61 +}
0 62 \ No newline at end of file
... ...