Commit 9d080046d04cf46c1d3c8429f72e0c760893b5c1
1 parent
d1d32220
feat:视频跟读逻辑
Showing
11 changed files
with
242 additions
and
1 deletions
assets/images/gendubeij.png
0 → 100644
48.7 KB
assets/images/gendubeij_mengban.png
0 → 100644
23.5 KB
assets/images/star_dark.png
0 → 100644
2.88 KB
assets/images/star_light.png
0 → 100644
2.08 KB
lib/home/home_page.dart
| @@ -25,7 +25,7 @@ class HomePage extends StatelessWidget { | @@ -25,7 +25,7 @@ class HomePage extends StatelessWidget { | ||
| 25 | class _HomePageView extends StatelessWidget { | 25 | class _HomePageView extends StatelessWidget { |
| 26 | void _headerActionEvent(HeaderActionType type) { | 26 | void _headerActionEvent(HeaderActionType type) { |
| 27 | if (type == HeaderActionType.video) { | 27 | if (type == HeaderActionType.video) { |
| 28 | - | 28 | + Navigator.of(AppRouter.context).pushNamed(AppRouteName.reAfter); |
| 29 | } else if (type == HeaderActionType.phase) { | 29 | } else if (type == HeaderActionType.phase) { |
| 30 | Navigator.of(AppRouter.context).pushNamed(AppRouteName.lesson); | 30 | Navigator.of(AppRouter.context).pushNamed(AppRouteName.lesson); |
| 31 | } else if (type == HeaderActionType.listen) { | 31 | } else if (type == HeaderActionType.listen) { |
lib/repeatafter/bloc/repeat_after_bloc.dart
0 → 100644
| 1 | +import 'package:flutter/cupertino.dart'; | ||
| 2 | +import 'package:flutter_bloc/flutter_bloc.dart'; | ||
| 3 | +import 'package:flutter_easyloading/flutter_easyloading.dart'; | ||
| 4 | + | ||
| 5 | +part 'repeat_after_event.dart'; | ||
| 6 | +part 'repeat_after_state.dart'; | ||
| 7 | + | ||
| 8 | +class RepeatAfterBloc extends Bloc<RepeatAfterEvent, RepeatAfterState> { | ||
| 9 | + RepeatAfterBloc() : super(RepeatAfterInitial()) { | ||
| 10 | + on<RepeatAfterEvent>((event, emit) { | ||
| 11 | + // TODO: implement event handler | ||
| 12 | + }); | ||
| 13 | + } | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + Future<void> requestData() async { | ||
| 17 | + EasyLoading.show(); | ||
| 18 | + Future.delayed(const Duration(milliseconds: 2000),(){ | ||
| 19 | + EasyLoading.dismiss(); | ||
| 20 | + emit(RequestDataState()); | ||
| 21 | + }); | ||
| 22 | + } | ||
| 23 | +} |
lib/repeatafter/bloc/repeat_after_event.dart
0 → 100644
lib/repeatafter/bloc/repeat_after_state.dart
0 → 100644
lib/repeatafter/repeat_after_page.dart
0 → 100644
| 1 | +import 'dart:math'; | ||
| 2 | + | ||
| 3 | +import 'package:flutter/material.dart'; | ||
| 4 | +import 'package:flutter_bloc/flutter_bloc.dart'; | ||
| 5 | +import 'package:flutter_easyloading/flutter_easyloading.dart'; | ||
| 6 | +import 'package:wow_english/common/widgets/we_app_bar.dart'; | ||
| 7 | +import 'package:wow_english/repeatafter/widgets/repeat_after_item.dart'; | ||
| 8 | + | ||
| 9 | +import 'bloc/repeat_after_bloc.dart'; | ||
| 10 | + | ||
| 11 | +class RepeatAfterPage extends StatelessWidget { | ||
| 12 | + const RepeatAfterPage({super.key}); | ||
| 13 | + | ||
| 14 | + @override | ||
| 15 | + Widget build(BuildContext context) { | ||
| 16 | + return BlocProvider( | ||
| 17 | + create: (context) => RepeatAfterBloc()..requestData(), | ||
| 18 | + child: _RepeatAfterPageView(), | ||
| 19 | + ); | ||
| 20 | + } | ||
| 21 | +} | ||
| 22 | + | ||
| 23 | +class _RepeatAfterPageView extends StatelessWidget { | ||
| 24 | + @override | ||
| 25 | + Widget build(BuildContext context) { | ||
| 26 | + return BlocListener<RepeatAfterBloc, RepeatAfterState>( | ||
| 27 | + listener: (context, state) { | ||
| 28 | + if (state is RequestDataState) { | ||
| 29 | + EasyLoading.showToast('网络请求结束'); | ||
| 30 | + } | ||
| 31 | + }, | ||
| 32 | + child: _repeatAfterView(), | ||
| 33 | + ); | ||
| 34 | + } | ||
| 35 | + | ||
| 36 | + Widget _repeatAfterView() => BlocBuilder<RepeatAfterBloc, RepeatAfterState>( | ||
| 37 | + builder: (context, state) { | ||
| 38 | + return Scaffold( | ||
| 39 | + appBar: const WEAppBar( | ||
| 40 | + titleText: '视频跟读', | ||
| 41 | + centerTitle: false, | ||
| 42 | + ), | ||
| 43 | + body: SafeArea( | ||
| 44 | + child: Container( | ||
| 45 | + alignment: Alignment.center, | ||
| 46 | + child: ListView.builder( | ||
| 47 | + itemCount: 10, | ||
| 48 | + scrollDirection: Axis.horizontal, | ||
| 49 | + itemBuilder: (BuildContext context,int index){ | ||
| 50 | + bool unLock = index%3==0; | ||
| 51 | + return RepeatAfterItem( | ||
| 52 | + unLock: unLock, | ||
| 53 | + tapEvent: () { | ||
| 54 | + | ||
| 55 | + }, | ||
| 56 | + starNumber: !unLock?0:Random().nextInt(5) | ||
| 57 | + ); | ||
| 58 | + }), | ||
| 59 | + ), | ||
| 60 | + ), | ||
| 61 | + ); | ||
| 62 | + }, | ||
| 63 | + ); | ||
| 64 | +} | ||
| 65 | + |
lib/repeatafter/widgets/repeat_after_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 RepeatAfterItem extends StatelessWidget { | ||
| 6 | + const RepeatAfterItem({super.key, required this.starNumber, required this.unLock, required this.tapEvent}); | ||
| 7 | + //分数 | ||
| 8 | + final int starNumber; | ||
| 9 | + //是否解锁 | ||
| 10 | + final bool unLock; | ||
| 11 | + | ||
| 12 | + final Function() tapEvent; | ||
| 13 | + | ||
| 14 | + @override | ||
| 15 | + Widget build(BuildContext context) { | ||
| 16 | + return Padding( | ||
| 17 | + padding: EdgeInsets.symmetric( | ||
| 18 | + horizontal: 10.w | ||
| 19 | + ), | ||
| 20 | + child: GestureDetector( | ||
| 21 | + onTap: (){ | ||
| 22 | + if(unLock) { | ||
| 23 | + tapEvent(); | ||
| 24 | + } | ||
| 25 | + }, | ||
| 26 | + child: Stack( | ||
| 27 | + children: [ | ||
| 28 | + _modelInfoWidget(context), | ||
| 29 | + _lockWidget() | ||
| 30 | + ], | ||
| 31 | + ), | ||
| 32 | + ), | ||
| 33 | + ); | ||
| 34 | + } | ||
| 35 | + | ||
| 36 | + Widget _modelInfoWidget(BuildContext context) { | ||
| 37 | + return Container( | ||
| 38 | + width: 162.w, | ||
| 39 | + height: 235.h, | ||
| 40 | + decoration: BoxDecoration( | ||
| 41 | + image: DecorationImage( | ||
| 42 | + image: AssetImage( | ||
| 43 | + 'gendubeij'.assetPng | ||
| 44 | + ), | ||
| 45 | + fit: BoxFit.fill | ||
| 46 | + ) | ||
| 47 | + ), | ||
| 48 | + padding: EdgeInsets.symmetric(horizontal: 11.w,vertical: 13.h), | ||
| 49 | + alignment: Alignment.center, | ||
| 50 | + child: Column( | ||
| 51 | + mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||
| 52 | + children: [ | ||
| 53 | + Image.network( | ||
| 54 | + 'https://img.liblibai.com/web/648331d033b41.png?image_process=format,webp&x-oss-process=image/resize,w_2980,m_lfit/format,webp', | ||
| 55 | + height: 100.h, | ||
| 56 | + width: 140.w, | ||
| 57 | + fit: BoxFit.fitWidth, | ||
| 58 | + ), | ||
| 59 | + Row( | ||
| 60 | + mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||
| 61 | + children: [ | ||
| 62 | + Image.asset( | ||
| 63 | + starNumber >= 1 ? 'star_light'.assetPng:'star_dark'.assetPng, | ||
| 64 | + width: 23.w, | ||
| 65 | + height: 21.h, | ||
| 66 | + ), | ||
| 67 | + Image.asset( | ||
| 68 | + starNumber >= 2 ? 'star_light'.assetPng:'star_dark'.assetPng, | ||
| 69 | + width: 23.w, | ||
| 70 | + height: 21.h, | ||
| 71 | + ), | ||
| 72 | + Image.asset( | ||
| 73 | + starNumber >= 3 ? 'star_light'.assetPng:'star_dark'.assetPng, | ||
| 74 | + width: 23.w, | ||
| 75 | + height: 21.h, | ||
| 76 | + ), | ||
| 77 | + Image.asset( | ||
| 78 | + starNumber >= 4 ? 'star_light'.assetPng:'star_dark'.assetPng, | ||
| 79 | + width: 23.w, | ||
| 80 | + height: 21.h, | ||
| 81 | + ), | ||
| 82 | + Image.asset( | ||
| 83 | + starNumber >= 5 ? 'star_light'.assetPng:'star_dark'.assetPng, | ||
| 84 | + width: 23.w, | ||
| 85 | + height: 21.h, | ||
| 86 | + ), | ||
| 87 | + ], | ||
| 88 | + ), | ||
| 89 | + Container( | ||
| 90 | + height: 35.h, | ||
| 91 | + width: double.infinity, | ||
| 92 | + decoration: BoxDecoration( | ||
| 93 | + color: const Color(0xFFFFCC00), | ||
| 94 | + borderRadius: BorderRadius.circular(5.r), | ||
| 95 | + border: Border.all( | ||
| 96 | + width: 1.0, | ||
| 97 | + color: const Color(0xFF333333), | ||
| 98 | + ), | ||
| 99 | + ), | ||
| 100 | + alignment: Alignment.center, | ||
| 101 | + child: Text( | ||
| 102 | + 'video title', | ||
| 103 | + style: TextStyle( | ||
| 104 | + fontSize: 16.sp, | ||
| 105 | + color: const Color(0xFF333333) | ||
| 106 | + ), | ||
| 107 | + ), | ||
| 108 | + ) | ||
| 109 | + ], | ||
| 110 | + ), | ||
| 111 | + ); | ||
| 112 | + } | ||
| 113 | + | ||
| 114 | + Widget _lockWidget() { | ||
| 115 | + return Offstage( | ||
| 116 | + offstage: unLock, | ||
| 117 | + child: Container( | ||
| 118 | + width: 162.w, | ||
| 119 | + height: 235.h, | ||
| 120 | + decoration: BoxDecoration( | ||
| 121 | + image: DecorationImage( | ||
| 122 | + image: AssetImage( | ||
| 123 | + 'gendubeij_mengban'.assetPng | ||
| 124 | + ), | ||
| 125 | + fit: BoxFit.fill | ||
| 126 | + ) | ||
| 127 | + ), | ||
| 128 | + alignment: Alignment.center, | ||
| 129 | + child: Image.asset( | ||
| 130 | + 'listen_lock'.assetPng, | ||
| 131 | + height: 36.h, | ||
| 132 | + width: 41.w, | ||
| 133 | + ), | ||
| 134 | + ), | ||
| 135 | + ); | ||
| 136 | + } | ||
| 137 | +} | ||
| 0 | \ No newline at end of file | 138 | \ No newline at end of file |
lib/route/route.dart
| @@ -8,6 +8,7 @@ import 'package:wow_english/listen/listen_page.dart'; | @@ -8,6 +8,7 @@ import 'package:wow_english/listen/listen_page.dart'; | ||
| 8 | import 'package:wow_english/login/forgetpwd/forget_password_home_page.dart'; | 8 | import 'package:wow_english/login/forgetpwd/forget_password_home_page.dart'; |
| 9 | import 'package:wow_english/login/loginpage/login_page.dart'; | 9 | import 'package:wow_english/login/loginpage/login_page.dart'; |
| 10 | import 'package:wow_english/login/setpwd/set_pwd_page.dart'; | 10 | import 'package:wow_english/login/setpwd/set_pwd_page.dart'; |
| 11 | +import 'package:wow_english/repeatafter/repeat_after_page.dart'; | ||
| 11 | import 'package:wow_english/shop/exchane/exchange_lesson_page.dart'; | 12 | 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/exchangelist/exchange_lesson_list_page.dart'; |
| 13 | import 'package:wow_english/shop/home/shop_home_page.dart'; | 14 | import 'package:wow_english/shop/home/shop_home_page.dart'; |
| @@ -26,6 +27,7 @@ class AppRouteName { | @@ -26,6 +27,7 @@ class AppRouteName { | ||
| 26 | static const String shop = 'shop'; | 27 | static const String shop = 'shop'; |
| 27 | static const String exLesson = 'exLesson'; | 28 | static const String exLesson = 'exLesson'; |
| 28 | static const String exList = 'exList'; | 29 | static const String exList = 'exList'; |
| 30 | + static const String reAfter = 'reAfter'; | ||
| 29 | static const String tab = '/'; | 31 | static const String tab = '/'; |
| 30 | } | 32 | } |
| 31 | 33 | ||
| @@ -58,6 +60,8 @@ class AppRouter { | @@ -58,6 +60,8 @@ class AppRouter { | ||
| 58 | return CupertinoPageRoute(builder: (_) => const ExchangeLessonPage()); | 60 | return CupertinoPageRoute(builder: (_) => const ExchangeLessonPage()); |
| 59 | case AppRouteName.exList: | 61 | case AppRouteName.exList: |
| 60 | return CupertinoPageRoute(builder: (_) => const ExchangeLessonListPage()); | 62 | return CupertinoPageRoute(builder: (_) => const ExchangeLessonListPage()); |
| 63 | + case AppRouteName.reAfter: | ||
| 64 | + return CupertinoPageRoute(builder: (_) => const RepeatAfterPage()); | ||
| 61 | case AppRouteName.setPwd: | 65 | case AppRouteName.setPwd: |
| 62 | final phoneNum = (settings.arguments as Map)['phoneNumber'] as String; | 66 | final phoneNum = (settings.arguments as Map)['phoneNumber'] as String; |
| 63 | return CupertinoPageRoute(builder: (_) => SetPassWordPage(phoneNum: phoneNum)); | 67 | return CupertinoPageRoute(builder: (_) => SetPassWordPage(phoneNum: phoneNum)); |