Commit 6d61919a27d0d1918de3bf7658e66cb66139d83b

Authored by 吴启风
1 parent 07599105

feat:增加过渡页&集成串联游戏

android/app/build.gradle
... ... @@ -29,7 +29,7 @@ android {
29 29 namespace "com.kouyuxingqiu.wow_english"
30 30 // compileSdkVersion flutter.compileSdkVersion
31 31 compileSdkVersion 33
32   - // 展示没有ndk需求
  32 + // 暂时没有ndk需求
33 33 // ndkVersion flutter.ndkVersion
34 34  
35 35 compileOptions {
... ... @@ -87,4 +87,5 @@ dependencies {
87 87 implementation 'com.geyifeng.immersionbar:immersionbar:3.2.2'
88 88 // kotlin扩展(可选)
89 89 implementation 'com.geyifeng.immersionbar:immersionbar-ktx:3.2.2'
  90 + implementation 'io.keyss.android.library:bjgame:1.0.2'
90 91 }
... ...
android/app/src/main/kotlin/com/kouyuxingqiu/wow_english/MainActivity.kt
... ... @@ -8,6 +8,7 @@ import androidx.core.view.WindowInsetsCompat
8 8 import androidx.core.view.WindowInsetsControllerCompat
9 9 import com.gyf.immersionbar.BarHide
10 10 import com.gyf.immersionbar.ImmersionBar
  11 +import com.kouyuxingqiu.wow_english.methodChannels.GameMethodChannel
11 12 import com.kouyuxingqiu.wow_english.methodChannels.SingSoungMethodChannel
12 13 import io.flutter.embedding.android.FlutterActivity
13 14  
... ... @@ -17,7 +18,10 @@ class MainActivity : FlutterActivity() {
17 18 Log.i("WowEnglish", "MainActivity onCreate")
18 19 //隐藏状态栏和导航栏
19 20 ImmersionBar.with(this).hideBar(BarHide.FLAG_HIDE_BAR).init()
20   - flutterEngine?.let { SingSoungMethodChannel(this, it) }
  21 + flutterEngine?.let {
  22 + SingSoungMethodChannel(this, it)
  23 + GameMethodChannel(this, it)
  24 + }
21 25 }
22 26  
23 27 override fun onResume() {
... ...
android/app/src/main/kotlin/com/kouyuxingqiu/wow_english/methodChannels/GameMethodChannel.kt 0 → 100644
  1 +package com.kouyuxingqiu.wow_english.methodChannels
  2 +
  3 +import android.content.Intent
  4 +import android.content.Intent.getIntent
  5 +import android.util.Log
  6 +import androidx.core.content.ContextCompat.startActivity
  7 +import com.kouyuxingqiu.wow_english.singsound.SingEngineHelper
  8 +import com.kouyuxingqiu.wow_english.util.GlobalHandler
  9 +import io.flutter.embedding.android.FlutterActivity
  10 +import io.flutter.embedding.engine.FlutterEngine
  11 +import io.flutter.plugin.common.MethodChannel
  12 +import org.cocos2dx.cpp.AppActivity
  13 +import java.lang.ref.WeakReference
  14 +
  15 +
  16 +/**
  17 + * @author: stay
  18 + * @date: 2024/4/22 18:36
  19 + * @description: 游戏方法通道
  20 + */
  21 +class GameMethodChannel(activity: FlutterActivity, flutterEngine: FlutterEngine) {
  22 + private var methodChannel: MethodChannel? = null
  23 + private val TAG = "GameMethodChannel"
  24 +
  25 + companion object {
  26 + var channel: WeakReference<GameMethodChannel>? = null
  27 +
  28 + fun invokeMethod(method: String, arguments: Any?) {
  29 + channel?.get()?.methodChannel?.invokeMethod(method, arguments)
  30 + }
  31 + }
  32 +
  33 + init {
  34 + // name需与flutter端一致
  35 + methodChannel =
  36 + MethodChannel(
  37 + flutterEngine.dartExecutor.binaryMessenger,
  38 + "wow_english/game_method_channel"
  39 + )
  40 + methodChannel?.setMethodCallHandler { call, result ->
  41 + Log.d(TAG, "call=${call.method} ${call.arguments} result=$result")
  42 + when (call.method) {
  43 + "openGamePage" -> {
  44 + if (call.hasArgument("gameId")) {
  45 + val gameId = call.argument<Int>("gameId")
  46 + activity.startActivity(
  47 + Intent(activity, AppActivity::class.java).apply {
  48 + putExtra("game", gameId)
  49 + })
  50 + result.success(true)
  51 + } else {
  52 + result.success(false)
  53 + }
  54 + }
  55 + }
  56 + }
  57 + channel = WeakReference(this)
  58 + }
  59 +}
0 60 \ No newline at end of file
... ...
android/build.gradle
... ... @@ -17,6 +17,7 @@ allprojects {
17 17 google()
18 18 mavenCentral()
19 19 maven { url 'https://repo.singsound.com/repository/singsound_ginger_android_sdk/' }
  20 + maven { url 'https://maven.zjzxsl.com/repository/android-public/' }
20 21 }
21 22 }
22 23  
... ...
assets/images/bg_frame_module_pic.png 0 → 100644

54.1 KB

assets/images/bg_header_sliver.png 0 → 100644

120 KB

assets/images/ic_countdown.png 0 → 100644

2.1 KB

assets/images/label_module_game.png 0 → 100644

15.7 KB

assets/images/label_module_study.png 0 → 100644

15.9 KB

assets/images/pic_module_game.png 0 → 100644

217 KB

assets/images/pic_module_study.png 0 → 100644

248 KB

lib/app/splash_page.dart
... ... @@ -69,7 +69,7 @@ class _TransitionViewState extends State&lt;TransitionView&gt; {
69 69 } else {
70 70 pushNamedAndRemoveUntil(AppRouteName.login, (route) => false);
71 71 }*/
72   - pushNamedAndRemoveUntil(AppRouteName.home, (route) => false);
  72 + pushNamedAndRemoveUntil(AppRouteName.moduleSelect, (route) => false);
73 73 });
74 74 }
75 75  
... ...
lib/pages/moduleSelect/bloc.dart 0 → 100644
  1 +import 'package:bloc/bloc.dart';
  2 +
  3 +import 'event.dart';
  4 +import 'state.dart';
  5 +
  6 +
  7 +class ModuleSelectBloc extends Bloc<ModuleSelectEvent, ModuleSelectState> {
  8 +
  9 + ModuleSelectBloc() : super(ModuleSelectState().init()) {
  10 + on<InitEvent>(_init);
  11 + }
  12 +
  13 + void _init(InitEvent event, Emitter<ModuleSelectState> emit) async {
  14 + emit(state.clone());
  15 + }
  16 +}
... ...
lib/pages/moduleSelect/event.dart 0 → 100644
  1 +abstract class ModuleSelectEvent {}
  2 +
  3 +class InitEvent extends ModuleSelectEvent {}
0 4 \ No newline at end of file
... ...
lib/pages/moduleSelect/state.dart 0 → 100644
  1 +class ModuleSelectState {
  2 + ModuleSelectState init() {
  3 + return ModuleSelectState();
  4 + }
  5 +
  6 + ModuleSelectState clone() {
  7 + return ModuleSelectState();
  8 + }
  9 +}
... ...
lib/pages/moduleSelect/view.dart 0 → 100644
  1 +import 'package:flutter/material.dart';
  2 +import 'package:flutter_bloc/flutter_bloc.dart';
  3 +import 'package:wow_english/common/extension/string_extension.dart';
  4 +import 'package:wow_english/pages/moduleSelect/state.dart';
  5 +import 'package:wow_english/pages/moduleSelect/widgets/BaseHomeHeaderWidget.dart';
  6 +
  7 +import 'bloc.dart';
  8 +import 'event.dart';
  9 +import 'package:flutter_screenutil/flutter_screenutil.dart';
  10 +import 'package:wow_english/route/route.dart';
  11 +
  12 +class ModuleSelectPage extends StatelessWidget {
  13 + const ModuleSelectPage({super.key});
  14 +
  15 + @override
  16 + Widget build(BuildContext context) {
  17 + return BlocProvider(
  18 + create: (BuildContext context) => ModuleSelectBloc()..add(InitEvent()),
  19 + child: Builder(builder: (context) => _HomePageView()),
  20 + );
  21 + }
  22 +}
  23 +
  24 +class _HomePageView extends StatelessWidget {
  25 + @override
  26 + Widget build(BuildContext context) {
  27 + final bloc = BlocProvider.of<ModuleSelectBloc>(context);
  28 + return BlocListener<ModuleSelectBloc, ModuleSelectState>(
  29 + listener: (context, state) {},
  30 + child: _homeView(),
  31 + );
  32 + }
  33 +
  34 + Widget _homeView() => BlocBuilder<ModuleSelectBloc, ModuleSelectState>(
  35 + builder: (context, state) {
  36 + final bloc = BlocProvider.of<ModuleSelectBloc>(context);
  37 + return Scaffold(
  38 + body: Container(
  39 + color: Colors.white,
  40 + child: Column(
  41 + children: [
  42 + const BaseHomeHeaderWidget(),
  43 + Expanded(
  44 + child: Center(
  45 + child: Row(
  46 + children: [
  47 + Expanded(
  48 + child: GestureDetector(
  49 + onTap: () {
  50 + pushNamed(AppRouteName.home);
  51 + },
  52 + child: Column(
  53 + mainAxisAlignment: MainAxisAlignment.center,
  54 + children: [
  55 + Image.asset('pic_module_study'.assetPng,
  56 + width: 162.5.w, height: 203.5.h),
  57 + 10.verticalSpace,
  58 + Image.asset('label_module_study'.assetPng,
  59 + width: 124.w, height: 34.h),
  60 + ],
  61 + ),
  62 + ),
  63 + ),
  64 + Expanded(
  65 + child: GestureDetector(
  66 + onTap: () {
  67 + pushNamed(AppRouteName.games);
  68 + },
  69 + child: Column(
  70 + mainAxisAlignment: MainAxisAlignment.center,
  71 + children: [
  72 + Image.asset('pic_module_game'.assetPng,
  73 + width: 162.5.w, height: 203.5.h),
  74 + 10.verticalSpace,
  75 + Image.asset('label_module_game'.assetPng,
  76 + width: 124.w, height: 34.h),
  77 + ],
  78 + )),
  79 + ),
  80 + ],
  81 + ),
  82 + ),
  83 + )
  84 + ],
  85 + ),
  86 + ),
  87 + );
  88 + });
  89 +}
... ...
lib/pages/moduleSelect/widgets/BaseHomeHeaderWidget.dart 0 → 100644
  1 +import 'package:flutter/material.dart';
  2 +import 'package:flutter_bloc/flutter_bloc.dart';
  3 +import 'package:flutter_screenutil/flutter_screenutil.dart';
  4 +import 'package:wow_english/common/extension/string_extension.dart';
  5 +
  6 +import '../../../common/core/user_util.dart';
  7 +import '../../../models/course_entity.dart';
  8 +import '../../../route/route.dart';
  9 +import '../../../utils/image_util.dart';
  10 +import '../../user/bloc/user_bloc.dart';
  11 +
  12 +class BaseHomeHeaderWidget extends StatelessWidget {
  13 + const BaseHomeHeaderWidget({super.key, this.entity});
  14 +
  15 + final CourseEntity? entity;
  16 +
  17 + @override
  18 + Widget build(BuildContext context) {
  19 + return BlocBuilder<UserBloc, UserState>(
  20 + builder: (context, state) {
  21 + return Container(
  22 + height: 45,
  23 + width: double.infinity,
  24 + decoration: BoxDecoration(
  25 + image: DecorationImage(
  26 + image: AssetImage('bg_header_sliver'.assetPng),
  27 + fit: BoxFit.cover,
  28 + ),
  29 + ),
  30 + padding: EdgeInsets.symmetric(horizontal: 9.5.w),
  31 + child: Row(
  32 + children: [
  33 + ScreenUtil().bottomBarHeight.horizontalSpace,
  34 + GestureDetector(
  35 + onTap: () => {onUserClick()},
  36 + child: Container(
  37 + decoration: BoxDecoration(
  38 + border: Border.all(
  39 + width: 1.0,
  40 + color: const Color(0xFF140C10),
  41 + ),
  42 + borderRadius: BorderRadius.circular(21),
  43 + ),
  44 + child: CircleAvatar(
  45 + radius: 21,
  46 + backgroundImage: ImageUtil.getImageProviderOnDefault(
  47 + UserUtil.getUser()?.avatarUrl),
  48 + ),
  49 + ),
  50 + ),
  51 + GestureDetector(
  52 + onTap: () {
  53 + onUserClick();
  54 + },
  55 + child: Container(
  56 + margin: const EdgeInsets.only(left: 7),
  57 + padding: const EdgeInsets.all(4.0),
  58 + decoration: BoxDecoration(
  59 + color: Colors.white,
  60 + borderRadius: BorderRadius.circular(2),
  61 + border: Border.all(
  62 + width: 1.0,
  63 + color: const Color(0xFF140C10),
  64 + style: BorderStyle.solid),
  65 + ),
  66 + child: Text(
  67 + UserUtil.getUser()?.name ?? '未登录',
  68 + style: TextStyle(
  69 + color: const Color(0xFF333333), fontSize: 16.sp),
  70 + ),
  71 + ),
  72 + ),
  73 + 20.horizontalSpace,
  74 + const Expanded(
  75 + child: Text(
  76 + "WOW ENGLISH",
  77 + textAlign: TextAlign.left,
  78 + style: TextStyle(color: Colors.white, fontSize: 30.0),
  79 + )),
  80 + Row(children: <Widget>[
  81 + Image(
  82 + width: 20.0.w,
  83 + height: 20.0.h,
  84 + image: AssetImage('ic_countdown'.assetPng)),
  85 + // 替换为你的图片资源路径
  86 + const SizedBox(width: 10.0),
  87 + // 图片和文本之间的间隔
  88 + const Text('还剩29天'),
  89 + ]),
  90 + ScreenUtil().bottomBarHeight.horizontalSpace,
  91 + ],
  92 + ));
  93 + },
  94 + );
  95 + }
  96 +}
  97 +
  98 +void onUserClick() {
  99 + if (UserUtil.token.isEmpty) {
  100 + pushNamed(AppRouteName.login);
  101 + } else {
  102 + pushNamed(AppRouteName.user);
  103 + }
  104 +}
... ...
lib/pages/shop/home/widgets/product_item.dart 0 → 100644
  1 +import 'package:cached_network_image/cached_network_image.dart';
  2 +import 'package:flutter/material.dart';
  3 +import 'package:flutter_screenutil/flutter_screenutil.dart';
  4 +import 'package:wow_english/models/product_entity.dart';
  5 +
  6 +class ProductItem extends StatelessWidget {
  7 + const ProductItem({super.key,
  8 + required this.onTap, this.entity});
  9 +
  10 + final ProductEntity? entity;
  11 +
  12 + final Function() onTap;
  13 +
  14 + @override
  15 + Widget build(BuildContext context) {
  16 + return Container(
  17 + decoration: BoxDecoration(
  18 + borderRadius: BorderRadius.circular(10.r),
  19 + border: Border.all(
  20 + width: 1.0,
  21 + color: Colors.black
  22 + )
  23 + // image: DecorationImage(
  24 + // image: AssetImage(
  25 + // ''.assetPng,
  26 + // ),
  27 + // fit: BoxFit.fill
  28 + // )
  29 + ),
  30 + padding: EdgeInsets.symmetric(horizontal: 16.w,vertical: 16.h),
  31 + child: Row(
  32 + mainAxisAlignment: MainAxisAlignment.spaceBetween,
  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,
  63 + width: 124.w,
  64 + ),
  65 + 21.5.horizontalSpace,
  66 + Expanded(
  67 + child: Column(
  68 + mainAxisAlignment: MainAxisAlignment.spaceBetween,
  69 + crossAxisAlignment: CrossAxisAlignment.end,
  70 + children: [
  71 + Text(
  72 + entity?.name ?? '',
  73 + softWrap: true,
  74 + textAlign: TextAlign.left,
  75 + style: TextStyle(
  76 + fontSize: 12.sp,
  77 + color: const Color(0xFF333333)
  78 + ),
  79 + ),
  80 + RichText(
  81 + text: TextSpan(
  82 + children:[
  83 + TextSpan(
  84 + text: '¥',
  85 + style: TextStyle(
  86 + fontSize: 21.sp,
  87 + color: const Color(0xFFF51A1A),
  88 + )
  89 + ),
  90 + TextSpan(
  91 + text: entity?.price?.toString() ?? '',
  92 + style: TextStyle(
  93 + fontSize: 40.sp,
  94 + color: const Color(0xFFF51A1A),
  95 + ),
  96 + )
  97 + ]
  98 + ),
  99 + ),
  100 + GestureDetector(
  101 + onTap: () {
  102 + onTap();
  103 + },
  104 + child: Container(
  105 + decoration: BoxDecoration(
  106 + color: const Color(0xFFF5C51F),
  107 + borderRadius: BorderRadius.circular(5.r),
  108 + border: Border.all(
  109 + color: const Color(0xFF333333),
  110 + width: 1.0,
  111 + )
  112 + ),
  113 + padding: EdgeInsets.symmetric(
  114 + vertical: 1.h,
  115 + horizontal: 26.5.w,
  116 + ),
  117 + child: Text(
  118 + '立即购买',
  119 + style: TextStyle(
  120 + fontSize: 10.sp,
  121 + color: const Color(0xFF333333)
  122 + ),
  123 + ),
  124 + ),
  125 + )
  126 + ],
  127 + ),
  128 + )
  129 + ],
  130 + ),
  131 + );
  132 + }
  133 +}
0 134 \ No newline at end of file
... ...
lib/route/route.dart
... ... @@ -3,12 +3,14 @@ import &#39;package:flutter/material.dart&#39;;
3 3 import 'package:wow_english/app/splash_page.dart';
4 4 import 'package:wow_english/common/pages/wow_web_page.dart';
5 5 import 'package:wow_english/models/product_entity.dart';
  6 +import 'package:wow_english/pages/games/view.dart';
6 7 import 'package:wow_english/pages/home/home_page.dart';
7 8 import 'package:wow_english/pages/lessons/lesson_page.dart';
8 9 import 'package:wow_english/pages/listen/listen_page.dart';
9 10 import 'package:wow_english/pages/login/forgetpwd/forget_password_home_page.dart';
10 11 import 'package:wow_english/pages/login/loginpage/login_page.dart';
11 12 import 'package:wow_english/pages/login/setpwd/set_pwd_page.dart';
  13 +import 'package:wow_english/pages/moduleSelect/view.dart';
12 14 import 'package:wow_english/pages/practice/topic_picture_page.dart';
13 15 import 'package:wow_english/pages/repeatafter/repeat_after_page.dart';
14 16 import 'package:wow_english/pages/repeataftercontent/repeat_after_content_page.dart';
... ... @@ -32,6 +34,8 @@ import &#39;../utils/log_util.dart&#39;;
32 34 class AppRouteName {
33 35 static const String splash = 'splash';
34 36 static const String login = 'login';
  37 + static const String moduleSelect = 'moduleSelect';
  38 + static const String games = 'games';
35 39 static const String home = 'home';
36 40 static const String fogPwd = 'fogPwd';
37 41  
... ... @@ -88,6 +92,10 @@ class AppRouter {
88 92 transitionsBuilder: (_, __, ___, child) => child);
89 93 case AppRouteName.login:
90 94 return CupertinoPageRoute(builder: (_) => const LoginPage());
  95 + case AppRouteName.moduleSelect:
  96 + return CupertinoPageRoute(builder: (_) => const ModuleSelectPage());
  97 + case AppRouteName.games:
  98 + return CupertinoPageRoute(builder: (_) => const GamesPage());
91 99 case AppRouteName.home:
92 100 var moduleId = '';
93 101 if (settings.arguments != null) {
... ...