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 7  
8 8 String get assetPng => '$assetImg.png';
9 9  
  10 + String get assetWebp => '$assetImg.webp';
  11 +
10 12 String get assetGif => '$assetImg.gif';
11 13 }
12 14  
... ...
lib/pages/reading/bloc/reading_bloc.dart
1 1 import 'package:flutter/cupertino.dart';
2 2 import 'package:flutter_bloc/flutter_bloc.dart';
  3 +import 'package:wow_english/pages/reading/widgets/ReadingModeType.dart';
3 4  
4 5 part 'reading_event.dart';
5 6 part 'reading_state.dart';
6 7  
7 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 2  
3 3 @immutable
4 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 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 4 abstract class ReadingPageState {}
5 5  
6 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 2 import 'package:flutter_bloc/flutter_bloc.dart';
3 3 import 'package:flutter_screenutil/flutter_screenutil.dart';
4 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 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 12 @override
13 13 Widget build(BuildContext context) {
14 14 return BlocProvider(
15   - create: (_) => ReadingPageBloc(),
  15 + create: (_) => ReadingPageBloc(PageController()),
16 16 child: _ReadingPage(),
17 17 );
18 18 }
... ... @@ -23,73 +23,160 @@ class _ReadingPage extends StatelessWidget {
23 23 Widget build(BuildContext context) {
24 24 return BlocListener<ReadingPageBloc, ReadingPageState>(
25 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 33 final bloc = BlocProvider.of<ReadingPageBloc>(context);
33 34 return Container(
34 35 color: Colors.white,
35 36 child: Stack(
36 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 175 children: [
71 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 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 21 import 'package:wow_english/pages/video/lookvideo/look_video_page.dart';
22 22 import 'package:wow_english/pages/voiceanswer/voice_answer_page.dart';
23 23  
  24 +import '../pages/reading/reading_page.dart';
  25 +
24 26 class AppRouteName {
25 27 static const String splash = 'splash';
26 28 static const String login = 'login';
... ... @@ -41,6 +43,7 @@ class AppRouteName {
41 43 static const String voiceAnswer = 'voiceAnswer';
42 44 static const String user = 'user';
43 45 static const String lookVideo = 'lookVideo';
  46 + static const String reading = 'reading'; ///绘本
44 47 static const String tab = '/';
45 48 }
46 49  
... ... @@ -113,6 +116,8 @@ class AppRouter {
113 116 transitionDuration: Duration.zero,
114 117 pageBuilder: (_, __, ___) => const TabPage(),
115 118 transitionsBuilder: (_, __, ___, child) => child);
  119 + case AppRouteName.reading:
  120 + return CupertinoPageRoute(builder: (_) => const ReadingPage());
116 121 default:
117 122 return CupertinoPageRoute(
118 123 builder: (_) => Scaffold(body: Center(child: Text('No route defined for ${settings.name}'))));
... ...