Commit 2a427e12162f9bc5c394a9feb826a5f4379cd83e

Authored by 吴启风
1 parent 1dc46cf7

feat:绘本静态ui基本完成

assets/images/bg_reading_mode.png 0 → 100644

16 KB

assets/images/record_pause.webp 0 → 100644
No preview for this file type
assets/images/record_play.webp 0 → 100644
No preview for this file type
lib/common/extension/string_extension.dart
@@ -7,6 +7,8 @@ extension AssetExtension on String { @@ -7,6 +7,8 @@ extension AssetExtension on String {
7 7
8 String get assetPng => '$assetImg.png'; 8 String get assetPng => '$assetImg.png';
9 9
  10 + String get assetWebp => '$assetImg.webp';
  11 +
10 String get assetGif => '$assetImg.gif'; 12 String get assetGif => '$assetImg.gif';
11 } 13 }
12 14
lib/pages/reading/bloc/reading_bloc.dart
1 import 'package:flutter/cupertino.dart'; 1 import 'package:flutter/cupertino.dart';
2 import 'package:flutter_bloc/flutter_bloc.dart'; 2 import 'package:flutter_bloc/flutter_bloc.dart';
  3 +import 'package:wow_english/pages/reading/widgets/ReadingModeType.dart';
3 4
4 part 'reading_event.dart'; 5 part 'reading_event.dart';
5 part 'reading_state.dart'; 6 part 'reading_state.dart';
6 7
7 class ReadingPageBloc extends Bloc<ReadingPageEvent, ReadingPageState> { 8 class ReadingPageBloc extends Bloc<ReadingPageEvent, ReadingPageState> {
8 - ReadingPageBloc() : super(ReadingPageInitial()) {  
9 - on<ReadingPageEvent>((event, emit) {  
10 - // TODO: implement event handler  
11 - }); 9 +
  10 + final PageController pageController;
  11 +
  12 + ///当前页索引
  13 + int _currentPage = 0;
  14 +
  15 + ///当前播放模式
  16 + ReadingModeType _currentMode = ReadingModeType.auto;
  17 +
  18 + int get currentPage => _currentPage + 1;
  19 +
  20 + ReadingModeType get currentMode => _currentMode;
  21 +
  22 + ReadingPageBloc(this.pageController) : super(ReadingPageInitial()) {
  23 + on<CurrentPageIndexChangeEvent>(_pageControllerChange);
  24 + on<CurrentModeChangeEvent>(_selectItemLoad);
  25 + // pageController.addListener(() {
  26 + // _currentPage = pageController.page!.round();
  27 + // });
  28 + }
  29 +
  30 + @override
  31 + Future<void> close() {
  32 + pageController.dispose();
  33 + return super.close();
  34 + }
  35 +
  36 + void _pageControllerChange(CurrentPageIndexChangeEvent event, Emitter<ReadingPageState> emitter) async {
  37 + _currentPage = event.pageIndex;
  38 + emitter(CurrentPageIndexState());
  39 + }
  40 +
  41 + void _selectItemLoad(CurrentModeChangeEvent event, Emitter<ReadingPageState> emitter) async {
  42 + if (_currentMode == ReadingModeType.auto) {
  43 + _currentMode = ReadingModeType.manual;
  44 + } else {
  45 + _currentMode = ReadingModeType.auto;
  46 + }
  47 + emitter(CurrentModeState());
12 } 48 }
13 } 49 }
lib/pages/reading/bloc/reading_event.dart
@@ -2,3 +2,10 @@ part of &#39;reading_bloc.dart&#39;; @@ -2,3 +2,10 @@ part of &#39;reading_bloc.dart&#39;;
2 2
3 @immutable 3 @immutable
4 abstract class ReadingPageEvent {} 4 abstract class ReadingPageEvent {}
  5 +
  6 +class CurrentPageIndexChangeEvent extends ReadingPageEvent {
  7 + final int pageIndex;
  8 + CurrentPageIndexChangeEvent(this.pageIndex);
  9 +}
  10 +
  11 +class CurrentModeChangeEvent extends ReadingPageEvent {}
5 \ No newline at end of file 12 \ No newline at end of file
lib/pages/reading/bloc/reading_state.dart
@@ -4,3 +4,8 @@ part of &#39;reading_bloc.dart&#39;; @@ -4,3 +4,8 @@ part of &#39;reading_bloc.dart&#39;;
4 abstract class ReadingPageState {} 4 abstract class ReadingPageState {}
5 5
6 class ReadingPageInitial extends ReadingPageState {} 6 class ReadingPageInitial extends ReadingPageState {}
  7 +
  8 +class CurrentPageIndexState extends ReadingPageState {}
  9 +
  10 +/// 手动or自动播放
  11 +class CurrentModeState extends ReadingPageState {}
lib/pages/reading/reading_page.dart
@@ -2,17 +2,17 @@ import &#39;package:flutter/material.dart&#39;; @@ -2,17 +2,17 @@ import &#39;package:flutter/material.dart&#39;;
2 import 'package:flutter_bloc/flutter_bloc.dart'; 2 import 'package:flutter_bloc/flutter_bloc.dart';
3 import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 import 'package:flutter_screenutil/flutter_screenutil.dart';
4 import 'package:wow_english/common/extension/string_extension.dart'; 4 import 'package:wow_english/common/extension/string_extension.dart';
5 -import 'package:wow_english/pages/practice/widgets/practice_header_widget.dart'; 5 +import 'package:wow_english/pages/reading/widgets/ReadingModeType.dart';
6 6
7 import 'bloc/reading_bloc.dart'; 7 import 'bloc/reading_bloc.dart';
8 8
9 -class ReadingItemPage extends StatelessWidget {  
10 - const ReadingItemPage({super.key}); 9 +class ReadingPage extends StatelessWidget {
  10 + const ReadingPage({super.key});
11 11
12 @override 12 @override
13 Widget build(BuildContext context) { 13 Widget build(BuildContext context) {
14 return BlocProvider( 14 return BlocProvider(
15 - create: (_) => ReadingPageBloc(), 15 + create: (_) => ReadingPageBloc(PageController()),
16 child: _ReadingPage(), 16 child: _ReadingPage(),
17 ); 17 );
18 } 18 }
@@ -23,73 +23,160 @@ class _ReadingPage extends StatelessWidget { @@ -23,73 +23,160 @@ class _ReadingPage extends StatelessWidget {
23 Widget build(BuildContext context) { 23 Widget build(BuildContext context) {
24 return BlocListener<ReadingPageBloc, ReadingPageState>( 24 return BlocListener<ReadingPageBloc, ReadingPageState>(
25 listener: (context, state) {}, 25 listener: (context, state) {},
26 - child: _voiceAnswerView(), 26 + child: _readingPageView(),
27 ); 27 );
28 } 28 }
29 29
30 - Widget _voiceAnswerView() =>  
31 - BlocBuilder<ReadingPageBloc, ReadingPageState>(builder: (context, state) { 30 + Widget _readingPageView() => BlocBuilder<ReadingPageBloc, ReadingPageState>(
  31 + buildWhen: (_, s) => s is CurrentPageIndexState,
  32 + builder: (context, state) {
32 final bloc = BlocProvider.of<ReadingPageBloc>(context); 33 final bloc = BlocProvider.of<ReadingPageBloc>(context);
33 return Container( 34 return Container(
34 color: Colors.white, 35 color: Colors.white,
35 child: Stack( 36 child: Stack(
36 children: [ 37 children: [
37 - Positioned(  
38 - left: 0,  
39 - right: 0,  
40 - bottom: 0,  
41 - child: Image.asset(  
42 - 'bottom_grass'.assetPng,  
43 - fit: BoxFit.fitWidth,  
44 - )),  
45 - Column(  
46 - children: [  
47 - PracticeHeaderWidget(  
48 - title: '1/8',  
49 - onTap: () {  
50 - Navigator.pop(context);  
51 - }, 38 + PageView.builder(
  39 + itemCount: 10,
  40 + controller: bloc.pageController,
  41 + onPageChanged: (int index) {
  42 + bloc.add(CurrentPageIndexChangeEvent(index));
  43 + },
  44 + itemBuilder: (context, int index) {
  45 + return _readingPagerItem();
  46 + }),
  47 + Container(
  48 + color: Colors.transparent,
  49 + height: 60.h,
  50 + child: Row(
  51 + mainAxisAlignment: MainAxisAlignment.spaceBetween,
  52 + children: [
  53 + Padding(
  54 + padding:
  55 + EdgeInsets.only(left: ScreenUtil().bottomBarHeight),
  56 + child: IconButton(
  57 + onPressed: () {
  58 + Navigator.pop(context);
  59 + },
  60 + icon: Image.asset(
  61 + 'back_around'.assetPng,
  62 + width: 40,
  63 + height: 40,
  64 + )),
  65 + ),
  66 + Container(
  67 + height: 32.h,
  68 + padding: EdgeInsets.symmetric(horizontal: 27.w),
  69 + decoration: BoxDecoration(
  70 + color: const Color(0xFF00B6F1),
  71 + borderRadius: BorderRadius.circular(15.r),
  72 + border: Border.all(
  73 + width: 1.0,
  74 + color: const Color(0xFF140C10),
  75 + ),
  76 + ),
  77 + alignment: Alignment.center,
  78 + child: Text(
  79 + '${bloc.currentPage}/10',
  80 +
  81 + ///todo 分母需要替换成数据数组长度
  82 + style: TextStyle(fontSize: 20.sp, color: Colors.white),
  83 + ),
  84 + ),
  85 +
  86 + Padding(
  87 + padding: EdgeInsets.only(right: 15.w + ScreenUtil().bottomBarHeight),
  88 + child: GestureDetector(
  89 + onTap: () {
  90 + bloc.add(CurrentModeChangeEvent());
  91 + },
  92 + child: SizedBox(
  93 + height: 40.h,
  94 + child: Container(
  95 + decoration: BoxDecoration(
  96 + image: DecorationImage(
  97 + image: AssetImage('bg_reading_mode'.assetPng),
  98 + fit: BoxFit.fill),
  99 + ),
  100 + alignment: Alignment.center,
  101 + padding: EdgeInsets.symmetric(horizontal: 10.w),
  102 + child: Text(
  103 + bloc.currentMode == ReadingModeType.auto
  104 + ? '设为手动'
  105 + : '设为自动',
  106 + style: TextStyle(fontSize: 14.5.sp),
  107 + ),
  108 + ),
  109 + ),
  110 + ),
  111 + ),
  112 +
  113 + // ScreenUtil().bottomBarHeight.horizontalSpace,
  114 + ],
  115 + ),
  116 + ),
  117 + Align(
  118 + alignment: Alignment.bottomLeft,
  119 + child: Container(
  120 + color: const Color(0x4DFFFFFF),
  121 + margin: EdgeInsets.symmetric(horizontal: 10.w),
  122 + child: Row(
  123 + children: [
  124 + Image.asset(
  125 + 'voice'.assetPng,
  126 + height: 40.h,
  127 + width: 45.w,
  128 + ),
  129 + SizedBox(
  130 + width: 10.w,
  131 + ),
  132 + Expanded(
  133 + child: Text(
  134 + "HelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorld",
  135 + style: TextStyle(
  136 + color: const Color(0xFF333333), fontSize: 21.sp),
  137 + maxLines: 2,
  138 + overflow: TextOverflow.ellipsis,
  139 + )),
  140 + SizedBox(
  141 + width: 10.w,
  142 + ),
  143 + Image.asset(
  144 + 'micro_phone'.assetPng,
  145 + height: 47.h,
  146 + width: 47.w,
  147 + ),
  148 + SizedBox(
  149 + width: 10.w,
  150 + ),
  151 + Visibility(
  152 + visible: false,
  153 +
  154 + ///todo 依据是否录过音
  155 + child: Image.asset(
  156 + 'record_pause'.assetWebp,
  157 +
  158 + ///todo 根据播放状态切换图片
  159 + height: 33.h,
  160 + width: 33.w,
  161 + ),
  162 + )
  163 + ],
52 ), 164 ),
53 - Expanded(  
54 - child: PageView.builder(  
55 - itemCount: 10,  
56 - itemBuilder: (context, int index) {  
57 - return _voiceAnswerItem();  
58 - }))  
59 - ], 165 + ),
60 ) 166 )
61 ], 167 ],
62 ), 168 ),
63 ); 169 );
64 }); 170 });
65 171
66 - Widget _voiceAnswerItem() =>  
67 - BlocBuilder<VoiceAnswerBloc, VoiceAnswerState>(builder: (context, state) {  
68 - return Row(  
69 - mainAxisAlignment: MainAxisAlignment.center, 172 + Widget _readingPagerItem() =>
  173 + BlocBuilder<ReadingPageBloc, ReadingPageState>(builder: (context, state) {
  174 + return Stack(
70 children: [ 175 children: [
71 Image.network( 176 Image.network(
72 - 'https://img.liblibai.com/web/648331d5a2cb5.png?image_process=format,webp&x-oss-process=image/resize,w_2980,m_lfit/format,webp',  
73 - height: 186.h,  
74 - width: 186.w,  
75 - ),  
76 - 160.horizontalSpace,  
77 - Column(  
78 - mainAxisAlignment: MainAxisAlignment.center,  
79 - children: [  
80 - Image.asset(  
81 - 'voice'.assetPng,  
82 - height: 52.h,  
83 - width: 46.w,  
84 - ),  
85 - 70.verticalSpace,  
86 - Image.asset(  
87 - 'micro_phone'.assetPng,  
88 - height: 75.w,  
89 - width: 75.w,  
90 - )  
91 - ],  
92 - ) 177 + 'https://img.liblibai.com/web/648331d5a2cb5.png?image_process=format,webp&x-oss-process=image/resize,w_2980,m_lfit/format,webp',
  178 + height: double.infinity,
  179 + width: double.infinity),
93 ], 180 ],
94 ); 181 );
95 }); 182 });
lib/pages/reading/widgets/ReadingModeType.dart 0 → 100644
  1 +
  2 +/// Enum for reading mode type
  3 +enum ReadingModeType {
  4 + auto,
  5 + manual
  6 +}
0 \ No newline at end of file 7 \ No newline at end of file
lib/pages/reading/widgets/reading_header_widget.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 +/// 绘本页面的头部组件
  6 +class ReadingHeaderWidget extends StatelessWidget {
  7 + const ReadingHeaderWidget({super.key, required this.onTap, this.title = ''});
  8 +
  9 + final Function() onTap;
  10 +
  11 + final String title;
  12 +
  13 + @override
  14 + Widget build(BuildContext context) {
  15 + return Container(
  16 + color: Colors.white,
  17 + height: 60.h,
  18 + child: Row(
  19 + mainAxisAlignment: MainAxisAlignment.spaceBetween,
  20 + children: [
  21 + Padding(
  22 + padding: EdgeInsets.only(left: ScreenUtil().bottomBarHeight),
  23 + child: IconButton(
  24 + onPressed: () {
  25 + onTap();
  26 + },
  27 + icon: Image.asset(
  28 + 'back_around'.assetPng,
  29 + width: 40,
  30 + height: 40,
  31 + )),
  32 + ),
  33 + Container(
  34 + height: 32.h,
  35 + padding: EdgeInsets.symmetric(horizontal: 27.w),
  36 + decoration: BoxDecoration(
  37 + color: const Color(0xFF00B6F1),
  38 + borderRadius: BorderRadius.circular(15.r),
  39 + border: Border.all(
  40 + width: 1.0,
  41 + color: const Color(0xFF140C10),
  42 + ),
  43 + ),
  44 + alignment: Alignment.center,
  45 + child: Text(
  46 + title,
  47 + style: TextStyle(fontSize: 20.sp, color: Colors.white),
  48 + ),
  49 + ),
  50 + ScreenUtil().bottomBarHeight.horizontalSpace,
  51 + ],
  52 + ),
  53 + );
  54 + }
  55 +}
lib/route/route.dart
@@ -21,6 +21,8 @@ import &#39;package:wow_english/pages/user/user_page.dart&#39;; @@ -21,6 +21,8 @@ import &#39;package:wow_english/pages/user/user_page.dart&#39;;
21 import 'package:wow_english/pages/video/lookvideo/look_video_page.dart'; 21 import 'package:wow_english/pages/video/lookvideo/look_video_page.dart';
22 import 'package:wow_english/pages/voiceanswer/voice_answer_page.dart'; 22 import 'package:wow_english/pages/voiceanswer/voice_answer_page.dart';
23 23
  24 +import '../pages/reading/reading_page.dart';
  25 +
24 class AppRouteName { 26 class AppRouteName {
25 static const String splash = 'splash'; 27 static const String splash = 'splash';
26 static const String login = 'login'; 28 static const String login = 'login';
@@ -41,6 +43,7 @@ class AppRouteName { @@ -41,6 +43,7 @@ class AppRouteName {
41 static const String voiceAnswer = 'voiceAnswer'; 43 static const String voiceAnswer = 'voiceAnswer';
42 static const String user = 'user'; 44 static const String user = 'user';
43 static const String lookVideo = 'lookVideo'; 45 static const String lookVideo = 'lookVideo';
  46 + static const String reading = 'reading'; ///绘本
44 static const String tab = '/'; 47 static const String tab = '/';
45 } 48 }
46 49
@@ -113,6 +116,8 @@ class AppRouter { @@ -113,6 +116,8 @@ class AppRouter {
113 transitionDuration: Duration.zero, 116 transitionDuration: Duration.zero,
114 pageBuilder: (_, __, ___) => const TabPage(), 117 pageBuilder: (_, __, ___) => const TabPage(),
115 transitionsBuilder: (_, __, ___, child) => child); 118 transitionsBuilder: (_, __, ___, child) => child);
  119 + case AppRouteName.reading:
  120 + return CupertinoPageRoute(builder: (_) => const ReadingPage());
116 default: 121 default:
117 return CupertinoPageRoute( 122 return CupertinoPageRoute(
118 builder: (_) => Scaffold(body: Center(child: Text('No route defined for ${settings.name}')))); 123 builder: (_) => Scaffold(body: Center(child: Text('No route defined for ${settings.name}'))));