diff --git a/assets/images/background_grass.png b/assets/images/background_grass.png new file mode 100644 index 0000000..76103bd --- /dev/null +++ b/assets/images/background_grass.png diff --git a/assets/images/choose.png b/assets/images/choose.png new file mode 100644 index 0000000..dec077b --- /dev/null +++ b/assets/images/choose.png diff --git a/lib/home/home_page.dart b/lib/home/home_page.dart index c465e5e..1e12179 100644 --- a/lib/home/home_page.dart +++ b/lib/home/home_page.dart @@ -33,7 +33,8 @@ class _HomePageView extends StatelessWidget { } else if (type == HeaderActionType.shop) { Navigator.of(AppRouter.context).pushNamed(AppRouteName.shop); } else { - + // Navigator.of(AppRouter.context).pushNamed(AppRouteName.topicPic); + Navigator.of(AppRouter.context).pushNamed(AppRouteName.topicWord); } } diff --git a/lib/listen/bloc/listen_bloc.dart b/lib/listen/bloc/listen_bloc.dart index 73aa9fe..cf2c7b6 100644 --- a/lib/listen/bloc/listen_bloc.dart +++ b/lib/listen/bloc/listen_bloc.dart @@ -1,7 +1,6 @@ -import 'dart:async'; -import 'package:bloc/bloc.dart'; -import 'package:meta/meta.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; part 'listen_event.dart'; part 'listen_state.dart'; diff --git a/lib/listen/widgets/listen_item_widget.dart b/lib/listen/widgets/listen_item_widget.dart index 0089a93..3f73252 100644 --- a/lib/listen/widgets/listen_item_widget.dart +++ b/lib/listen/widgets/listen_item_widget.dart @@ -33,7 +33,7 @@ class ListenItemWidget extends StatelessWidget { fit: BoxFit.fill, ), Offstage( - offstage: isLock??false, + offstage: isLock, child: Container( height: 180.h, width: 180.h, diff --git a/lib/practice/chosetopic/topicpicture/bloc/topic_picture_bloc.dart b/lib/practice/chosetopic/topicpicture/bloc/topic_picture_bloc.dart index c7b2c59..1248801 100644 --- a/lib/practice/chosetopic/topicpicture/bloc/topic_picture_bloc.dart +++ b/lib/practice/chosetopic/topicpicture/bloc/topic_picture_bloc.dart @@ -1,4 +1,3 @@ - import 'package:flutter/cupertino.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -6,9 +5,37 @@ part 'topic_picture_event.dart'; part 'topic_picture_state.dart'; class TopicPictureBloc extends Bloc { - TopicPictureBloc() : super(TopicPictureInitial()) { - on((event, emit) { - // TODO: implement event handler - }); + + final PageController pageController; + + final int modelCount; + + int _currentPage = 0; + + int _selectItem = 0; + + int get currentPage => _currentPage + 1; + + int get selectItem => _selectItem; + + TopicPictureBloc(this.pageController, this.modelCount) : super(TopicPictureInitial()) { + on(_pageControllerChange); + on(_selectItemLoad); + } + + @override + Future close() { + pageController.dispose(); + return super.close(); + } + + void _pageControllerChange(CurrentPageIndexChangeEvent event,Emitter emitter) async { + _currentPage = event.pageIndex; + emitter(CurrentPageIndexState()); + } + + void _selectItemLoad(SelectItemEvent event,Emitter emitter) async { + _selectItem = event.selectIndex; + emitter(SelectItemChangeState()); } } diff --git a/lib/practice/chosetopic/topicpicture/bloc/topic_picture_event.dart b/lib/practice/chosetopic/topicpicture/bloc/topic_picture_event.dart index de2353c..781f545 100644 --- a/lib/practice/chosetopic/topicpicture/bloc/topic_picture_event.dart +++ b/lib/practice/chosetopic/topicpicture/bloc/topic_picture_event.dart @@ -2,3 +2,13 @@ part of 'topic_picture_bloc.dart'; @immutable abstract class TopicPictureEvent {} + +class CurrentPageIndexChangeEvent extends TopicPictureEvent { + final int pageIndex; + CurrentPageIndexChangeEvent(this.pageIndex); +} + +class SelectItemEvent extends TopicPictureEvent { + final int selectIndex; + SelectItemEvent(this.selectIndex); +} \ No newline at end of file diff --git a/lib/practice/chosetopic/topicpicture/bloc/topic_picture_state.dart b/lib/practice/chosetopic/topicpicture/bloc/topic_picture_state.dart index f8d3a2b..af0e103 100644 --- a/lib/practice/chosetopic/topicpicture/bloc/topic_picture_state.dart +++ b/lib/practice/chosetopic/topicpicture/bloc/topic_picture_state.dart @@ -4,3 +4,7 @@ part of 'topic_picture_bloc.dart'; abstract class TopicPictureState {} class TopicPictureInitial extends TopicPictureState {} + +class CurrentPageIndexState extends TopicPictureState {} + +class SelectItemChangeState extends TopicPictureState {} diff --git a/lib/practice/chosetopic/topicpicture/topic_picture_page.dart b/lib/practice/chosetopic/topicpicture/topic_picture_page.dart index c2980dc..6c5135b 100644 --- a/lib/practice/chosetopic/topicpicture/topic_picture_page.dart +++ b/lib/practice/chosetopic/topicpicture/topic_picture_page.dart @@ -1,11 +1,141 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:wow_english/common/extension/string_extension.dart'; +import 'package:wow_english/practice/chosetopic/topicpicture/bloc/topic_picture_bloc.dart'; + +import '../../widgets/practice_header_widget.dart'; class TopicPicturePage extends StatelessWidget { const TopicPicturePage({super.key}); @override Widget build(BuildContext context) { - // TODO: implement build - throw UnimplementedError(); + return BlocProvider( + create: (context) => TopicPictureBloc(PageController(),3), + child: _TopicPicturePage(), + ); + } +} + +class _TopicPicturePage extends StatelessWidget { + @override + Widget build(BuildContext context) { + return BlocListener( + listener: (context, state){}, + child: _topicPictureView(), + ); } + + Widget _topicPictureView() => BlocBuilder( + buildWhen: (_,s) => s is CurrentPageIndexState, + builder: (context,state){ + final bloc = BlocProvider.of(context); + return Container( + color: Colors.white, + child: Stack( + children: [ + Column( + children: [ + PracticeHeaderWidget( + title: '${bloc.currentPage}/8', + onTap: (){Navigator.pop(context);}, + ), + Expanded( + child: PageView.builder( + itemCount: 8, + scrollDirection: Axis.horizontal, + controller: bloc.pageController, + onPageChanged: (int index) { + bloc.add(CurrentPageIndexChangeEvent(index)); + }, + itemBuilder: (BuildContext context,int index){ + return _pageViewItemWidget(); + }), + ) + ], + ), + Positioned( + left: 0, + right: 0, + bottom: 0, + child: Image.asset('bottom_grass'.assetPng) + ) + ], + ), + ); + }); + + Widget _pageViewItemWidget() => BlocBuilder( + builder: (context, state){ + final bloc = BlocProvider.of(context); + return SafeArea( + child: Column( + children: [ + Text( + 'What to do when the sentence question is very long and needs a line break', + softWrap: true, + style: TextStyle( + fontSize: 21.sp, + color: const Color(0xFF333333) + ) + ), + 26.verticalSpace, + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Offstage( + offstage: (bloc.modelCount < 1), + child: _decodeImageWidget(1), + ), + Offstage( + offstage: (bloc.modelCount < 2), + child: _decodeImageWidget(2), + ), + Offstage( + offstage: (bloc.modelCount < 3), + child: _decodeImageWidget(3), + ), + Offstage( + offstage: (bloc.modelCount < 4), + child: _decodeImageWidget(4), + ) + ], + ) + ], + ), + ); + }); + + Widget _decodeImageWidget(int index) => BlocBuilder( + buildWhen: (_, s) => s is SelectItemChangeState, + builder: (context,state){ + final bloc = BlocProvider.of(context); + return GestureDetector( + onTap: () => bloc.add(SelectItemEvent(index)), + child: Container( + padding: const EdgeInsets.all(4.5), + decoration: BoxDecoration( + color: bloc.selectItem == index?const Color(0xFF00B6F1):Colors.white, + borderRadius: BorderRadius.circular(15), + ), + height: 143.h, + width: 143.w, + child: Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(15), + border: Border.all( + width: 1.0, + color: const Color(0xFF140C10) + ), + image: const DecorationImage( + fit: BoxFit.fitWidth, + image: NetworkImage('https://img1.baidu.com/it/u=3392591833,1640391553&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=714') + ) + ), + ), + ), + ); + }); } \ No newline at end of file diff --git a/lib/practice/chosetopic/topicword/bloc/topic_word_bloc.dart b/lib/practice/chosetopic/topicword/bloc/topic_word_bloc.dart new file mode 100644 index 0000000..3197025 --- /dev/null +++ b/lib/practice/chosetopic/topicword/bloc/topic_word_bloc.dart @@ -0,0 +1,41 @@ + +import 'package:flutter/cupertino.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +part 'topic_word_event.dart'; +part 'topic_word_state.dart'; + +class TopicWordBloc extends Bloc { + final PageController pageController; + + final int modelCount; + + int _currentPage = 0; + + int _selectItem = 0; + + int get currentPage => _currentPage + 1; + + int get selectItem => _selectItem; + + TopicWordBloc(this.pageController, this.modelCount) : super(TopicWordInitial()) { + on(_pageControllerChange); + on(_selectItemLoad); + } + + @override + Future close() { + pageController.dispose(); + return super.close(); + } + + void _pageControllerChange(CurrentPageIndexChangeEvent event,Emitter emitter) async { + _currentPage = event.pageIndex; + emitter(CurrentPageIndexState()); + } + + void _selectItemLoad(SelectItemEvent event,Emitter emitter) async { + _selectItem = event.selectIndex; + emitter(SelectItemChangeState()); + } +} diff --git a/lib/practice/chosetopic/topicword/bloc/topic_word_event.dart b/lib/practice/chosetopic/topicword/bloc/topic_word_event.dart new file mode 100644 index 0000000..3decdf5 --- /dev/null +++ b/lib/practice/chosetopic/topicword/bloc/topic_word_event.dart @@ -0,0 +1,14 @@ +part of 'topic_word_bloc.dart'; + +@immutable +abstract class TopicWordEvent {} + +class CurrentPageIndexChangeEvent extends TopicWordEvent { + final int pageIndex; + CurrentPageIndexChangeEvent(this.pageIndex); +} + +class SelectItemEvent extends TopicWordEvent { + final int selectIndex; + SelectItemEvent(this.selectIndex); +} \ No newline at end of file diff --git a/lib/practice/chosetopic/topicword/bloc/topic_word_state.dart b/lib/practice/chosetopic/topicword/bloc/topic_word_state.dart new file mode 100644 index 0000000..a27770f --- /dev/null +++ b/lib/practice/chosetopic/topicword/bloc/topic_word_state.dart @@ -0,0 +1,10 @@ +part of 'topic_word_bloc.dart'; + +@immutable +abstract class TopicWordState {} + +class TopicWordInitial extends TopicWordState {} + +class CurrentPageIndexState extends TopicWordState {} + +class SelectItemChangeState extends TopicWordState {} \ No newline at end of file diff --git a/lib/practice/chosetopic/topicword/topic_word_page.dart b/lib/practice/chosetopic/topicword/topic_word_page.dart new file mode 100644 index 0000000..90f6f97 --- /dev/null +++ b/lib/practice/chosetopic/topicword/topic_word_page.dart @@ -0,0 +1,162 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:wow_english/common/extension/string_extension.dart'; +import 'package:wow_english/practice/chosetopic/topicword/bloc/topic_word_bloc.dart'; +import 'package:wow_english/practice/widgets/practice_header_widget.dart'; + +class TopicWordPage extends StatelessWidget { + const TopicWordPage({super.key}); + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (context) => TopicWordBloc(PageController(), 3), + child: _TopicWordPage(), + ); + } +} + +class _TopicWordPage extends StatelessWidget { + @override + Widget build(BuildContext context) { + return BlocListener( + listener: (context, state) { + + }, + child: _topicWordView(), + ); + } + + Widget _topicWordView() => BlocBuilder( + builder: (context,state){ + final bloc = BlocProvider.of(context); + return Container( + color: Colors.white, + child: Stack( + children: [ + Positioned( + left: 0, + right: 0, + bottom: 0, + child: Image.asset('background_grass'.assetPng,fit: BoxFit.fitWidth,) + ), + Column( + children: [ + PracticeHeaderWidget( + title: '${bloc.currentPage}/8', + onTap: (){Navigator.pop(context);}, + ), + Expanded( + child: PageView.builder( + itemCount: 8, + scrollDirection: Axis.horizontal, + controller: bloc.pageController, + onPageChanged: (int index) { + bloc.add(CurrentPageIndexChangeEvent(index)); + }, + itemBuilder: (BuildContext context,int index){ + return _pageViewItemWidget(); + }), + ) + ], + ), + ], + ), + ); + }); + + Widget _pageViewItemWidget() => BlocBuilder( + builder: (context, state){ + final bloc = BlocProvider.of(context); + return SafeArea( + child: Column( + children: [ + Text( + 'What to do when the sentence question is very long and needs a line break', + softWrap: true, + style: TextStyle( + fontSize: 21.sp, + color: const Color(0xFF333333) + ) + ), + 26.verticalSpace, + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Offstage( + offstage: (bloc.modelCount < 1), + child: _decodeImageWidget(1), + ), + Offstage( + offstage: (bloc.modelCount < 2), + child: _decodeImageWidget(2), + ), + Offstage( + offstage: (bloc.modelCount < 3), + child: _decodeImageWidget(3), + ), + Offstage( + offstage: (bloc.modelCount < 4), + child: _decodeImageWidget(4), + ) + ], + ) + ], + ), + ); + }); + + Widget _decodeImageWidget(int index) => BlocBuilder( + buildWhen: (_, s) => s is SelectItemChangeState, + builder: (context,state){ + final bloc = BlocProvider.of(context); + return GestureDetector( + onTap: () => bloc.add(SelectItemEvent(index)), + child: Container( + width: 143.w, + height: 143.h, + padding: EdgeInsets.only(left: 13.w,right: 13.w,top: 13.h,bottom: 13.h), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(15), + border: Border.all( + width: 1.0, + color: const Color(0xFF140C10) + ), + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Expanded( + child: Container( + alignment: Alignment.center, + child: Text( + 'yellow', + style: TextStyle( + fontSize: 20.sp, + color: const Color(0xFF333333) + ) + ), + ), + ), + Container( + height: 30.h, + width: double.infinity, + decoration: BoxDecoration( + color: bloc.selectItem == index?const Color(0xFF00B6F1):Colors.white, + borderRadius: BorderRadius.circular(15.r), + border: Border.all( + width: 1.5, + color: const Color(0xFF140C10) + ), + ), + alignment: Alignment.center, + child: Image.asset('choose'.assetPng), + ) + ], + ), + ), + ); + }); +} \ No newline at end of file diff --git a/lib/practice/widgets/practice_header_widget.dart b/lib/practice/widgets/practice_header_widget.dart new file mode 100644 index 0000000..456f314 --- /dev/null +++ b/lib/practice/widgets/practice_header_widget.dart @@ -0,0 +1,59 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:wow_english/common/extension/string_extension.dart'; + +class PracticeHeaderWidget extends StatelessWidget { + const PracticeHeaderWidget({super.key, required this.onTap,this.title = ''}); + + final Function() onTap; + + final String title; + + @override + Widget build(BuildContext context) { + return Container( + color: Colors.white, + height: 60.h, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Padding( + padding: EdgeInsets.only( + left: ScreenUtil().bottomBarHeight + ), + child: IconButton( + onPressed: (){ + onTap(); + }, + icon: Image.asset( + 'back_around'.assetPng, + width: 40, + height: 40, + )), + ), + Container( + height: 40.h, + padding: EdgeInsets.symmetric(horizontal: 27.w), + decoration: BoxDecoration( + color: const Color(0xFF00B6F1), + borderRadius: BorderRadius.circular(20.r), + border: Border.all( + width: 1.0, + color: const Color(0xFF333333), + ), + ), + alignment: Alignment.center, + child: Text( + title, + style: TextStyle( + fontSize: 20.sp, + color: Colors.white + ), + ), + ), + ScreenUtil().bottomBarHeight.horizontalSpace, + ], + ), + ); + } +} \ No newline at end of file diff --git a/lib/route/route.dart b/lib/route/route.dart index 132b629..2b3fee6 100644 --- a/lib/route/route.dart +++ b/lib/route/route.dart @@ -8,6 +8,8 @@ import 'package:wow_english/listen/listen_page.dart'; import 'package:wow_english/login/forgetpwd/forget_password_home_page.dart'; import 'package:wow_english/login/loginpage/login_page.dart'; import 'package:wow_english/login/setpwd/set_pwd_page.dart'; +import 'package:wow_english/practice/chosetopic/topicpicture/topic_picture_page.dart'; +import 'package:wow_english/practice/chosetopic/topicword/topic_word_page.dart'; import 'package:wow_english/repeatafter/repeat_after_page.dart'; import 'package:wow_english/shop/exchane/exchange_lesson_page.dart'; import 'package:wow_english/shop/exchangelist/exchange_lesson_list_page.dart'; @@ -28,6 +30,8 @@ class AppRouteName { static const String exLesson = 'exLesson'; static const String exList = 'exList'; static const String reAfter = 'reAfter'; + static const String topicPic = 'topicPic'; + static const String topicWord = 'topicWord'; static const String tab = '/'; } @@ -62,6 +66,10 @@ class AppRouter { return CupertinoPageRoute(builder: (_) => const ExchangeLessonListPage()); case AppRouteName.reAfter: return CupertinoPageRoute(builder: (_) => const RepeatAfterPage()); + case AppRouteName.topicPic: + return CupertinoPageRoute(builder: (_) => const TopicPicturePage()); + case AppRouteName.topicWord: + return CupertinoPageRoute(builder: (_) => const TopicWordPage()); case AppRouteName.setPwd: final phoneNum = (settings.arguments as Map)['phoneNumber'] as String; return CupertinoPageRoute(builder: (_) => SetPassWordPage(phoneNum: phoneNum));