Commit 6d61919a27d0d1918de3bf7658e66cb66139d83b
1 parent
07599105
feat:增加过渡页&集成串联游戏
Showing
19 changed files
with
430 additions
and
3 deletions
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
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<TransitionView> { |
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
lib/pages/moduleSelect/state.dart
0 → 100644
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 '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 | 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 '../utils/log_util.dart'; |
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) { | ... | ... |