Commit 60e47f7c9f6568ae7c18cd87b81321790caec725

Authored by liangchengyou
1 parent 41b60caf

feat:课程选择功能

assets/images/back.png 0 → 100644

10.6 KB

lib/common/widgets/we_app_bar.dart
1 1 import 'package:flutter/material.dart';
  2 +import 'package:wow_english/common/extension/string_extension.dart';
2 3  
3 4 class WEAppBar extends StatelessWidget implements PreferredSizeWidget {
4 5 final String? titleText;
... ... @@ -6,12 +7,16 @@ class WEAppBar extends StatelessWidget implements PreferredSizeWidget {
6 7 final VoidCallback? onBack;
7 8 final Color? backgroundColor;
8 9 final PreferredSizeWidget? bottom;
  10 + final Widget? leading;
  11 + final List<Widget>? actions;
9 12 const WEAppBar({
10 13 this.titleText,
11 14 this.centerTitle = true,
12 15 this.onBack,
13 16 this.backgroundColor,
14 17 this.bottom,
  18 + this.leading,
  19 + this.actions,
15 20 super.key});
16 21  
17 22 @override
... ... @@ -19,7 +24,21 @@ class WEAppBar extends StatelessWidget implements PreferredSizeWidget {
19 24 return AppBar(
20 25 centerTitle: centerTitle,
21 26 title: Text(titleText??''),
22   - backgroundColor: backgroundColor??Theme.of(context).colorScheme.inversePrimary,
  27 + leading: leading??GestureDetector(
  28 + onTap: () {
  29 + Navigator.pop(context);
  30 + },
  31 + child: Container(
  32 + alignment: Alignment.center,
  33 + child: Image.asset(
  34 + 'back'.assetPng,
  35 + height: 43,
  36 + width: 43,
  37 + ),
  38 + ),
  39 + ),
  40 + backgroundColor: backgroundColor??Colors.white,
  41 + actions: actions??[],
23 42 );
24 43 }
25 44  
... ...
lib/home/bloc/home_bloc.dart 0 → 100644
  1 +import 'dart:async';
  2 +
  3 +import 'package:bloc/bloc.dart';
  4 +import 'package:meta/meta.dart';
  5 +
  6 +part 'home_event.dart';
  7 +part 'home_state.dart';
  8 +
  9 +class HomeBloc extends Bloc<HomeEvent, HomeState> {
  10 + HomeBloc() : super(HomeInitial()) {
  11 + on<HomeEvent>((event, emit) {
  12 + // TODO: implement event handler
  13 + });
  14 + }
  15 +}
... ...
lib/home/bloc/home_event.dart 0 → 100644
  1 +part of 'home_bloc.dart';
  2 +
  3 +@immutable
  4 +abstract class HomeEvent {}
... ...
lib/home/bloc/home_state.dart 0 → 100644
  1 +part of 'home_bloc.dart';
  2 +
  3 +@immutable
  4 +abstract class HomeState {}
  5 +
  6 +class HomeInitial extends HomeState {}
... ...
lib/home/home_page.dart
1 1 import 'package:flutter/material.dart';
  2 +import 'package:flutter_bloc/flutter_bloc.dart';
  3 +import 'package:wow_english/home/bloc/home_bloc.dart';
2 4 import 'package:wow_english/home/widgets/home_tab_header_widget.dart';
  5 +import 'package:wow_english/route/route.dart';
3 6  
4 7 class HomePage extends StatelessWidget {
5 8 const HomePage({super.key});
6 9  
7 10 @override
8 11 Widget build(BuildContext context) {
9   - return Scaffold(
10   - body: Container(
11   - color: Colors.white,
12   - child: const Center(
13   - child: Column(
14   - children: [
15   - HomeTabHeaderWidget(),
16   - ],
  12 + return BlocProvider(
  13 + create: (context) => HomeBloc(),
  14 + child: _HomePageView(),
  15 + );
  16 + }
  17 +}
  18 +
  19 +class _HomePageView extends StatelessWidget {
  20 + void _headerActionEvent(HeaderActionType type) {
  21 + if (type == HeaderActionType.video) {
  22 +
  23 + } else if (type == HeaderActionType.phase) {
  24 + Navigator.of(AppRouter.context).pushNamed(AppRouteName.lesson);
  25 + } else if (type == HeaderActionType.listen) {
  26 +
  27 + } else {
  28 +
  29 + }
  30 + }
  31 +
  32 + @override
  33 + Widget build(BuildContext context) {
  34 + return BlocListener<HomeBloc,HomeState>(
  35 + listener: (context, state){},
  36 + child: Scaffold(
  37 + body: Container(
  38 + color: Colors.white,
  39 + child: Center(
  40 + child: Column(
  41 + children: [
  42 + HomeTabHeaderWidget(
  43 + actionTap: (HeaderActionType type) {
  44 + _headerActionEvent(type);
  45 + },
  46 + ),
  47 + ],
  48 + ),
17 49 ),
18 50 ),
19 51 ),
... ...
lib/home/widgets/home_tab_header_widget.dart
... ... @@ -2,8 +2,22 @@ import &#39;package:flutter/material.dart&#39;;
2 2 import 'package:flutter_screenutil/flutter_screenutil.dart';
3 3 import 'package:wow_english/common/extension/string_extension.dart';
4 4  
  5 +enum HeaderActionType {
  6 + //视频跟读
  7 + video,
  8 + //阶段选择
  9 + phase,
  10 + //磨耳朵
  11 + listen,
  12 + //购买
  13 + shop,
  14 +}
  15 +
5 16 class HomeTabHeaderWidget extends StatelessWidget {
6   - const HomeTabHeaderWidget({super.key});
  17 +
  18 + const HomeTabHeaderWidget({super.key, this.actionTap});
  19 +
  20 + final Function(HeaderActionType type)? actionTap;
7 21  
8 22 @override
9 23 Widget build(BuildContext context) {
... ... @@ -42,19 +56,35 @@ class HomeTabHeaderWidget extends StatelessWidget {
42 56 )
43 57 ),
44 58 IconButton(
45   - onPressed: (){},
  59 + onPressed: (){
  60 + if(actionTap != null) {
  61 + actionTap!(HeaderActionType.video);
  62 + }
  63 + },
46 64 icon: Image.asset('video'.assetPng)
47 65 ),
48 66 IconButton(
49   - onPressed: (){},
  67 + onPressed: (){
  68 + if(actionTap != null) {
  69 + actionTap!(HeaderActionType.phase);
  70 + }
  71 + },
50 72 icon: Image.asset('home'.assetPng)
51 73 ),
52 74 IconButton(
53   - onPressed: (){},
  75 + onPressed: (){
  76 + if(actionTap != null) {
  77 + actionTap!(HeaderActionType.listen);
  78 + }
  79 + },
54 80 icon: Image.asset('listen'.assetPng)
55 81 ),
56 82 IconButton(
57   - onPressed: (){},
  83 + onPressed: (){
  84 + if(actionTap != null) {
  85 + actionTap!(HeaderActionType.shop);
  86 + }
  87 + },
58 88 icon: Image.asset('shop'.assetPng)
59 89 ),
60 90 ScreenUtil().bottomBarHeight.horizontalSpace,
... ...
lib/lessons/bloc/lesson_bloc.dart 0 → 100644
  1 +
  2 +import 'package:flutter/cupertino.dart';
  3 +import 'package:flutter_bloc/flutter_bloc.dart';
  4 +
  5 +part 'lesson_event.dart';
  6 +part 'lesson_state.dart';
  7 +
  8 +class LessonBloc extends Bloc<LessonEvent, LessonState> {
  9 +
  10 + final int pageIndex;
  11 +
  12 + final PageController pageController;
  13 +
  14 + int _currentPageIndex = 0;
  15 +
  16 + int get currentPageIndex => _currentPageIndex;
  17 +
  18 + LessonBloc(this.pageIndex,this.pageController) : super(LessonInitial()) {
  19 + _currentPageIndex = pageIndex;
  20 + on<PageViewChangeIndexEvent>(_pageIndexChange);
  21 + }
  22 +
  23 + void _pageIndexChange(PageViewChangeIndexEvent event,Emitter<LessonState> emitter) async {
  24 + _currentPageIndex = event.index;
  25 + emitter(PageIndexChangeState());
  26 + }
  27 +
  28 + @override
  29 + Future<void> close() {
  30 + pageController.dispose();
  31 + return super.close();
  32 + }
  33 +}
... ...
lib/lessons/bloc/lesson_event.dart 0 → 100644
  1 +part of 'lesson_bloc.dart';
  2 +
  3 +@immutable
  4 +abstract class LessonEvent {}
  5 +
  6 +class PageViewChangeIndexEvent extends LessonEvent {
  7 + final int index;
  8 + PageViewChangeIndexEvent(this.index);
  9 +}
... ...
lib/lessons/bloc/lesson_state.dart 0 → 100644
  1 +part of 'lesson_bloc.dart';
  2 +
  3 +@immutable
  4 +abstract class LessonState {}
  5 +
  6 +class LessonInitial extends LessonState {}
  7 +
  8 +class PageIndexChangeState extends LessonState {}
... ...
lib/lessons/lesson_page.dart 0 → 100644
  1 +import 'package:flutter/material.dart';
  2 +import 'package:flutter_bloc/flutter_bloc.dart';
  3 +import 'package:flutter_easyloading/flutter_easyloading.dart';
  4 +import 'package:flutter_screenutil/flutter_screenutil.dart';
  5 +import 'package:wow_english/common/extension/string_extension.dart';
  6 +import 'package:wow_english/common/widgets/we_app_bar.dart';
  7 +import 'package:wow_english/lessons/bloc/lesson_bloc.dart';
  8 +import 'package:wow_english/lessons/widgets/lesson_item_widget.dart';
  9 +
  10 +class LessonPage extends StatelessWidget {
  11 + const LessonPage({super.key, this.starPageIndex});
  12 +
  13 + final int? starPageIndex;
  14 +
  15 +
  16 + @override
  17 + Widget build(BuildContext context) {
  18 + return BlocProvider(
  19 + create: (context) => LessonBloc(
  20 + starPageIndex??0,
  21 + PageController(
  22 + initialPage: starPageIndex??0,
  23 + viewportFraction: 0.3
  24 + ),),
  25 + child: _LessonPageView(),
  26 + );
  27 + }
  28 +}
  29 +
  30 +class _LessonPageView extends StatelessWidget {
  31 +
  32 + final double _cardHeight = 240.0;
  33 +
  34 + final double _numItemHeight = 32.0;
  35 +
  36 + final double _scale = 0.8;
  37 +
  38 + @override
  39 + Widget build(BuildContext context) {
  40 + return BlocListener<LessonBloc,LessonState>(
  41 + listener: (context, state){},
  42 + child: Scaffold(
  43 + appBar: WEAppBar(
  44 + actions: <Widget>[
  45 + IconButton(
  46 + icon: Image.asset('shop'.assetPng),
  47 + color: Colors.white,
  48 + onPressed: () {
  49 + EasyLoading.showToast('购买');
  50 + },
  51 + )
  52 + ],
  53 + ),
  54 + body: _lessViewWidget(),
  55 + ),
  56 + );
  57 + }
  58 +
  59 + Widget _lessViewWidget() => BlocBuilder<LessonBloc,LessonState>(
  60 + builder: (context, state){
  61 + final bloc = BlocProvider.of<LessonBloc>(context);
  62 + return Center(
  63 + child: SafeArea(
  64 + child: Column(
  65 + children: [
  66 + SizedBox(
  67 + height: _cardHeight,
  68 + child: PageView.builder(
  69 + itemCount: 10,
  70 + controller: bloc.pageController,
  71 + onPageChanged: (int index) {
  72 + bloc.add(PageViewChangeIndexEvent(index));
  73 + },
  74 + itemBuilder: (context,index) => _itemTransCard(index)
  75 + ),
  76 + ),
  77 + 32.verticalSpace,
  78 + SizedBox(
  79 + height: 32,
  80 + width: 660,
  81 + child: ListView.builder(
  82 + itemCount: 10,
  83 + scrollDirection: Axis.horizontal,
  84 + itemBuilder: (BuildContext context,int index){
  85 + return Container(
  86 + height: 32,
  87 + width: 66,
  88 + padding: const EdgeInsets.symmetric(horizontal: 5),
  89 + child: GestureDetector(
  90 + onTap: () {
  91 + if (index == bloc.currentPageIndex) {
  92 + return;
  93 + }
  94 + int mill = (index - bloc.currentPageIndex) > 0 ? 100 * (index - bloc.currentPageIndex):100 * (bloc.currentPageIndex-index);
  95 + bloc.pageController.animateToPage(index, duration: Duration(milliseconds: mill), curve: Curves.ease);
  96 + },
  97 + child: Container(
  98 + height: bloc.currentPageIndex == index ? 32:20,
  99 + decoration: BoxDecoration(
  100 + color: bloc.currentPageIndex == index ? Colors.red:Colors.white,
  101 + border: Border.all(
  102 + width: 0.5,
  103 + color: Colors.black,
  104 + ),
  105 + ),
  106 + alignment: Alignment.center,
  107 + child: Text(
  108 + (index+1).toString(),
  109 + style: TextStyle(
  110 + color: bloc.currentPageIndex == index ? Colors.white:Colors.black
  111 + ),
  112 + ),
  113 + ),
  114 + ),
  115 + );
  116 + }),
  117 + // child: PageView.builder(
  118 + // itemCount: 10,
  119 + // controller: bloc.pageController,
  120 + // onPageChanged: (int index) {
  121 + // bloc.add(PageViewChangeIndexEvent(index));
  122 + // },
  123 + // itemBuilder: (context,index) => _itemNumCard(index)
  124 + // ),
  125 + )
  126 + ],
  127 + ),
  128 + ),
  129 + );
  130 + });
  131 +
  132 + Widget _itemTransCard(int index) => BlocBuilder<LessonBloc,LessonState>(
  133 + builder: (context, state) {
  134 + final bloc = BlocProvider.of<LessonBloc>(context);
  135 + Matrix4 matrix4 = Matrix4.identity();
  136 + if (index == bloc.currentPageIndex.floor()) {
  137 + //当前的item
  138 + double currScale = (1 - (bloc.currentPageIndex - index) * (1 - _scale)).toDouble();
  139 + var currTrans = _cardHeight * (1 - currScale) / 2;
  140 +
  141 + matrix4 = Matrix4.diagonal3Values(1.0, currScale, 1.0)
  142 + ..setTranslationRaw(0.0, currTrans, 0.0);
  143 + } else if (index == bloc.currentPageIndex.floor() + 1) {
  144 + //右边的item
  145 + var currScale = _scale + (bloc.currentPageIndex - index + 1) * (1 - _scale);
  146 + var currTrans = _cardHeight * (1 - currScale) / 2;
  147 +
  148 + matrix4 = Matrix4.diagonal3Values(1.0, currScale, 1.0)
  149 + ..setTranslationRaw(0.0, currTrans, 0.0);
  150 + } else if (index == bloc.currentPageIndex - 1) {
  151 + //左边
  152 + var currScale = (1 - (bloc.currentPageIndex - index) * (1 - _scale)).toDouble();
  153 + var currTrans = _cardHeight * (1 - currScale) / 2;
  154 +
  155 + matrix4 = Matrix4.diagonal3Values(1.0, currScale, 1.0)
  156 + ..setTranslationRaw(0.0, currTrans, 0.0);
  157 + } else {
  158 + //其他,不在屏幕显示的item
  159 + matrix4 = Matrix4.diagonal3Values(1.0, _scale, 1.0)
  160 + ..setTranslationRaw(0.0, _cardHeight * (1 - _scale) / 2, 0.0);
  161 + }
  162 +
  163 + return Transform(
  164 + transform: matrix4,
  165 + child: Padding(
  166 + padding: const EdgeInsets.symmetric(horizontal: 10),
  167 + child: LessonItemWidget(isSelected: bloc.currentPageIndex == index),
  168 + ),
  169 + );
  170 + });
  171 +
  172 + Widget _itemNumCard(int index) => BlocBuilder<LessonBloc,LessonState>(
  173 + builder: (context, state) {
  174 + final bloc = BlocProvider.of<LessonBloc>(context);
  175 + bool isSelected = bloc.currentPageIndex.floor() == index;
  176 + Matrix4 matrix4 = Matrix4.identity();
  177 + if (index == bloc.currentPageIndex.floor()) {
  178 + //当前的item
  179 + double currScale = (1 - (bloc.currentPageIndex - index) * (1 - _scale)).toDouble();
  180 + var currTrans = _numItemHeight * (1 - currScale) / 2;
  181 +
  182 + matrix4 = Matrix4.diagonal3Values(1.0, currScale, 1.0)
  183 + ..setTranslationRaw(0.0, currTrans, 0.0);
  184 + } else if (index == bloc.currentPageIndex.floor() + 1) {
  185 + //右边的item
  186 + var currScale = _scale + (bloc.currentPageIndex - index + 1) * (1 - _scale);
  187 + var currTrans = _numItemHeight * (1 - currScale) / 2;
  188 +
  189 + matrix4 = Matrix4.diagonal3Values(1.0, currScale, 1.0)
  190 + ..setTranslationRaw(0.0, currTrans, 0.0);
  191 + } else if (index == bloc.currentPageIndex - 1) {
  192 + //左边
  193 + var currScale = (1 - (bloc.currentPageIndex - index) * (1 - _scale)).toDouble();
  194 + var currTrans = _numItemHeight * (1 - currScale) / 2;
  195 +
  196 + matrix4 = Matrix4.diagonal3Values(1.0, currScale, 1.0)
  197 + ..setTranslationRaw(0.0, currTrans, 0.0);
  198 + } else {
  199 + //其他,不在屏幕显示的item
  200 + matrix4 = Matrix4.diagonal3Values(1.0, _scale, 1.0)
  201 + ..setTranslationRaw(0.0, _numItemHeight * (1 - _scale) / 2, 0.0);
  202 + }
  203 +
  204 + return Transform(
  205 + transform: matrix4,
  206 + child: Container(
  207 + color: isSelected?Colors.red:Colors.yellow,
  208 + ),
  209 + );
  210 + });
  211 +}
0 212 \ No newline at end of file
... ...
lib/lessons/widgets/lesson_item_widget.dart 0 → 100644
  1 +import 'package:flutter/material.dart';
  2 +import 'package:flutter_screenutil/flutter_screenutil.dart';
  3 +
  4 +class LessonItemWidget extends StatelessWidget {
  5 + const LessonItemWidget({super.key, required this.isSelected});
  6 + ///是否被选中
  7 + final bool isSelected;
  8 +
  9 + @override
  10 + Widget build(BuildContext context) {
  11 + return isSelected?_selectWidget():_unSelectWidget();
  12 + }
  13 +
  14 + Widget _unSelectWidget() {
  15 + return Container(
  16 + decoration: const BoxDecoration(
  17 + image: DecorationImage(
  18 + image: NetworkImage('https://img.liblibai.com/web/648331d033b41.png?image_process=format,webp&x-oss-process=image/resize,w_2980,m_lfit/format,webp'),
  19 + )
  20 + ),
  21 + );
  22 + }
  23 +
  24 + Widget _selectWidget() {
  25 + return Container(
  26 + padding: const EdgeInsets.all(10),
  27 + decoration: BoxDecoration(
  28 + border: Border.all(
  29 + width: 2.0,
  30 + color: Colors.red,
  31 + style: BorderStyle.solid
  32 + ),
  33 + ),
  34 + child: Column(
  35 + mainAxisAlignment: MainAxisAlignment.spaceBetween,
  36 + children: [
  37 + Expanded(
  38 + child: Image.network(
  39 + 'https://img.liblibai.com/web/648331d5a2cb5.png?image_process=format,webp&x-oss-process=image/resize,w_2980,m_lfit/format,webp',
  40 + ),
  41 + ),
  42 + 10.verticalSpace,
  43 + Container(
  44 + color: Colors.red,
  45 + width: double.infinity,
  46 + child: const Column(
  47 + children: [
  48 + Text('red'),
  49 + Text('第三段')
  50 + ],
  51 + ),
  52 + )
  53 + ],
  54 + ),
  55 + );
  56 + }
  57 +}
0 58 \ No newline at end of file
... ...
lib/login/loginpage/login_page.dart
... ... @@ -28,6 +28,7 @@ class _LoginPageView extends StatelessWidget {
28 28 listener: (context, state){
29 29 if (state is LoginResultChangeState) {
30 30 EasyLoading.showToast('登陆接口回调');
  31 + Navigator.of(context).pushNamed(AppRouteName.home);
31 32 }
32 33 },
33 34 child: _buildLoginViewWidget(),
... ...
lib/route/route.dart
... ... @@ -3,6 +3,7 @@ import &#39;package:flutter/material.dart&#39;;
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/home/home_page.dart';
  6 +import 'package:wow_english/lessons/lesson_page.dart';
6 7 import 'package:wow_english/login/forgetpwd/forget_password_home_page.dart';
7 8 import 'package:wow_english/login/loginpage/login_page.dart';
8 9 import 'package:wow_english/login/setpwd/set_pwd_page.dart';
... ... @@ -16,6 +17,7 @@ class AppRouteName {
16 17 static const String fogPwd = 'fogPwd';
17 18 static const String setPwd = 'setPwd';
18 19 static const String webView = 'webView';
  20 + static const String lesson = 'lesson';
19 21 static const String tab = '/';
20 22 }
21 23  
... ... @@ -38,6 +40,8 @@ class AppRouter {
38 40 return CupertinoPageRoute(builder: (_) => const HomePage());
39 41 case AppRouteName.fogPwd:
40 42 return CupertinoPageRoute(builder: (_) => const ForgetPasswordHomePage());
  43 + case AppRouteName.lesson:
  44 + return CupertinoPageRoute(builder: (_) => const LessonPage());
41 45 case AppRouteName.setPwd:
42 46 final phoneNum = (settings.arguments as Map)['phoneNumber'] as String;
43 47 return CupertinoPageRoute(builder: (_) => SetPassWordPage(phoneNum: phoneNum));
... ...