diff --git a/assets/images/alter.png b/assets/images/alter.png new file mode 100644 index 0000000..544a4bf --- /dev/null +++ b/assets/images/alter.png diff --git a/assets/images/intowow.png b/assets/images/intowow.png new file mode 100644 index 0000000..ebe7499 --- /dev/null +++ b/assets/images/intowow.png diff --git a/assets/images/oninto.png b/assets/images/oninto.png new file mode 100644 index 0000000..9e5ef6a --- /dev/null +++ b/assets/images/oninto.png diff --git a/ios/Podfile b/ios/Podfile index c573c29..2f4009f 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -43,5 +43,52 @@ end post_install do |installer| installer.pods_project.targets.each do |target| flutter_additional_ios_build_settings(target) + target.build_configurations.each do |config| + config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [ + '$(inherited)', + + ## dart: PermissionGroup.calendar + # 'PERMISSION_EVENTS=1', + + ## dart: PermissionGroup.reminders + # 'PERMISSION_REMINDERS=1', + + ## dart: PermissionGroup.contacts + # 'PERMISSION_CONTACTS=1', + + ## dart: PermissionGroup.camera + 'PERMISSION_CAMERA=1', + + ## dart: PermissionGroup.microphone + 'PERMISSION_MICROPHONE=1', + + ## dart: PermissionGroup.speech + # 'PERMISSION_SPEECH_RECOGNIZER=1', + + ## dart: PermissionGroup.photos + 'PERMISSION_PHOTOS=1', + + ## dart: [PermissionGroup.location, PermissionGroup.locationAlways, PermissionGroup.locationWhenInUse] + # 'PERMISSION_LOCATION=1', + + ## dart: PermissionGroup.notification + # 'PERMISSION_NOTIFICATIONS=1', + + ## dart: PermissionGroup.mediaLibrary + # 'PERMISSION_MEDIA_LIBRARY=1', + + ## dart: PermissionGroup.sensors + # 'PERMISSION_SENSORS=1', + + ## dart: PermissionGroup.bluetooth + # 'PERMISSION_BLUETOOTH=1', + + ## dart: PermissionGroup.appTrackingTransparency + # 'PERMISSION_APP_TRACKING_TRANSPARENCY=1', + + ## dart: PermissionGroup.criticalAlerts + # 'PERMISSION_CRITICAL_ALERTS=1' + ] + end end end diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index a191984..11de1df 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -24,6 +24,8 @@ ???? CFBundleVersion $(FLUTTER_BUILD_NUMBER) + LSApplicationCategoryType + LSRequiresIPhoneOS NSAppTransportSecurity @@ -31,6 +33,8 @@ NSAllowsArbitraryLoads + NSCameraUsageDescription + 需要访问相机完成拍照 NSMicrophoneUsageDescription 需要获取录音完成后续功能 NSPhotoLibraryUsageDescription diff --git a/lib/app/app.dart b/lib/app/app.dart index 6e425b2..7315ad0 100644 --- a/lib/app/app.dart +++ b/lib/app/app.dart @@ -7,6 +7,7 @@ import 'package:responsive_framework/responsive_breakpoints.dart'; import 'package:wow_english/common/blocs/cachebloc/cache_bloc.dart'; import 'package:wow_english/common/widgets/hide_keyboard_widget.dart'; import 'package:wow_english/pages/tab/blocs/tab_bloc.dart'; +import 'package:wow_english/pages/user/bloc/user_bloc.dart'; import 'package:wow_english/route/route.dart'; class App extends StatelessWidget { @@ -20,6 +21,7 @@ class App extends StatelessWidget { providers: [ BlocProvider(create: (_) => TabBloc()), BlocProvider(create: (_) => CacheBloc()), + BlocProvider(create: (_) => UserBloc()), ], child: HideKeyboard( child: MaterialApp( diff --git a/lib/pages/home/widgets/home_tab_header_widget.dart b/lib/pages/home/widgets/home_tab_header_widget.dart index 1a63632..f65798f 100644 --- a/lib/pages/home/widgets/home_tab_header_widget.dart +++ b/lib/pages/home/widgets/home_tab_header_widget.dart @@ -1,7 +1,9 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:wow_english/common/core/user_util.dart'; import 'package:wow_english/common/extension/string_extension.dart'; +import 'package:wow_english/pages/user/bloc/user_bloc.dart'; import 'package:wow_english/utils/color_util.dart'; import 'package:wow_english/utils/image_util.dart'; @@ -26,89 +28,93 @@ class HomeTabHeaderWidget extends StatelessWidget { @override Widget build(BuildContext context) { - return Container( - height: 45, - width: double.infinity, - color: HexColor(themColor??''), - padding: EdgeInsets.symmetric(horizontal: 9.5.w), - child: Row( - children: [ - ScreenUtil().bottomBarHeight.horizontalSpace, - GestureDetector( - onTap: () => actionTap?.call(HeaderActionType.user), - child: Container( - decoration: BoxDecoration( - border: Border.all( - width: 1.0, - color: const Color(0xFF140C10), + return BlocBuilder( + builder: (context,state) { + return Container( + height: 45, + width: double.infinity, + color: HexColor(themColor??''), + padding: EdgeInsets.symmetric(horizontal: 9.5.w), + child: Row( + children: [ + ScreenUtil().bottomBarHeight.horizontalSpace, + GestureDetector( + onTap: () => actionTap?.call(HeaderActionType.user), + child: Container( + decoration: BoxDecoration( + border: Border.all( + width: 1.0, + color: const Color(0xFF140C10), + ), + borderRadius: BorderRadius.circular(21), + ), + child: CircleAvatar( + radius: 21, + backgroundImage: ImageUtil.getImageProviderOnDefault(UserUtil.getUser()?.avatarUrl), + ), + ), ), - borderRadius: BorderRadius.circular(21), - ), - child: CircleAvatar( - radius: 21, - backgroundImage: ImageUtil.getImageProviderOnDefault(UserUtil.getUser()?.avatarUrl), - ), - ), - ), - GestureDetector( - onTap: () { - if (actionTap != null) { - actionTap!(HeaderActionType.user); - } - }, - child: Container( - margin: const EdgeInsets.only(left: 7), - padding: const EdgeInsets.all(4.0), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(2), - border: Border.all(width: 1.0, color: const Color(0xFF140C10), style: BorderStyle.solid), - ), - child: Text( - UserUtil.getUser()?.name ?? '未登录', - style: TextStyle(color: const Color(0xFF333333), fontSize: 16.sp), - ), - ), - ), - 20.horizontalSpace, - const Expanded( - child: Text( - 'learn wow', - textAlign: TextAlign.left, - style: TextStyle(color: Colors.white, fontSize: 30.0), - )), - IconButton( - onPressed: () { - if (actionTap != null) { - actionTap!(HeaderActionType.video); - } - }, - icon: Image.asset('video'.assetPng)), - IconButton( - onPressed: () { - if (actionTap != null) { - actionTap!(HeaderActionType.phase); - } - }, - icon: Image.asset('home'.assetPng)), - IconButton( - onPressed: () { - if (actionTap != null) { - actionTap!(HeaderActionType.listen); - } - }, - icon: Image.asset('listen'.assetPng)), - // IconButton( - // onPressed: (){ - // if(actionTap != null) { - // actionTap!(HeaderActionType.shop); - // } - // }, - // icon: Image.asset('shop'.assetPng) - // ), - ScreenUtil().bottomBarHeight.horizontalSpace, - ], - ) + GestureDetector( + onTap: () { + if (actionTap != null) { + actionTap!(HeaderActionType.user); + } + }, + child: Container( + margin: const EdgeInsets.only(left: 7), + padding: const EdgeInsets.all(4.0), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(2), + border: Border.all(width: 1.0, color: const Color(0xFF140C10), style: BorderStyle.solid), + ), + child: Text( + UserUtil.getUser()?.name ?? '未登录', + style: TextStyle(color: const Color(0xFF333333), fontSize: 16.sp), + ), + ), + ), + 20.horizontalSpace, + const Expanded( + child: Text( + 'learn wow', + textAlign: TextAlign.left, + style: TextStyle(color: Colors.white, fontSize: 30.0), + )), + IconButton( + onPressed: () { + if (actionTap != null) { + actionTap!(HeaderActionType.video); + } + }, + icon: Image.asset('video'.assetPng)), + IconButton( + onPressed: () { + if (actionTap != null) { + actionTap!(HeaderActionType.phase); + } + }, + icon: Image.asset('home'.assetPng)), + IconButton( + onPressed: () { + if (actionTap != null) { + actionTap!(HeaderActionType.listen); + } + }, + icon: Image.asset('listen'.assetPng)), + // IconButton( + // onPressed: (){ + // if(actionTap != null) { + // actionTap!(HeaderActionType.shop); + // } + // }, + // icon: Image.asset('shop'.assetPng) + // ), + ScreenUtil().bottomBarHeight.horizontalSpace, + ], + ) + ); + }, ); } } diff --git a/lib/pages/user/bloc/user_bloc.dart b/lib/pages/user/bloc/user_bloc.dart index 54b7ad3..4eec9f5 100644 --- a/lib/pages/user/bloc/user_bloc.dart +++ b/lib/pages/user/bloc/user_bloc.dart @@ -33,13 +33,13 @@ class UserBloc extends Bloc { } void _updateUser(UserUpdate event, Emitter emitter) async { - Log.d('_updateUser, event: ${event.type}, emitter.isDone: ${emitter.isDone}, text=${modifyTextController.text}'); + Log.d('_updateUser, event: ${event.type}, emitter.isDone: ${emitter.isDone}, text=${event.content}'); UserEntity user = UserUtil.getUser()!; switch (event.type) { case ModifyUserInformationType.avatar: break; case ModifyUserInformationType.name: - String name = modifyTextController.text; + String name = event.content; try { await UserDao.updateUserInfoField(name: name); // 修改成功,更新本地缓存及UI @@ -54,7 +54,7 @@ class UserBloc extends Bloc { try { int age; try { - age = modifyTextController.text as int; + age = int.parse(event.content); } catch (e) { Log.w(e.toString()); throw '年龄格式错误'; @@ -69,8 +69,9 @@ class UserBloc extends Bloc { break; case ModifyUserInformationType.gender: try { - await UserDao.updateUserInfoField(gender: tempGender); - user.gender = tempGender; + var gender = int.parse(event.content); + await UserDao.updateUserInfoField(gender: gender); + user.gender = gender; emitter(UserInfoUpdated()); } catch (e) { Log.e('_updateUser gender, e: $e'); diff --git a/lib/pages/user/bloc/user_event.dart b/lib/pages/user/bloc/user_event.dart index 50d41bd..69423e1 100644 --- a/lib/pages/user/bloc/user_event.dart +++ b/lib/pages/user/bloc/user_event.dart @@ -12,8 +12,9 @@ class UserDelete extends UserEvent {} class UserUpdate extends UserEvent { final ModifyUserInformationType type; + final String content; - UserUpdate(this.type); + UserUpdate(this.type,this.content); } class UserUIUpdate extends UserEvent { diff --git a/lib/pages/user/information/user_information_page.dart b/lib/pages/user/information/user_information_page.dart index febee7c..3f2b2fd 100644 --- a/lib/pages/user/information/user_information_page.dart +++ b/lib/pages/user/information/user_information_page.dart @@ -6,6 +6,7 @@ import 'package:wow_english/common/core/user_util.dart'; import 'package:wow_english/common/widgets/we_app_bar.dart'; import 'package:wow_english/models/user_entity.dart'; import 'package:wow_english/pages/user/bloc/user_bloc.dart'; +import 'package:wow_english/route/route.dart'; import 'package:wow_english/utils/image_util.dart'; import 'package:wow_english/utils/log_util.dart'; @@ -16,35 +17,19 @@ class UserInformationPage extends StatelessWidget { @override Widget build(BuildContext context) { - return BlocProvider( - create: (context) => UserBloc(), - child: const _UserInformationView(), - ); + return BlocBuilder( + builder: (context,state){ + return _UserInformationContentView(); + }); } } -class _UserInformationView extends StatelessWidget { - const _UserInformationView({super.key}); - - @override - Widget build(BuildContext context) { - return BlocListener( - listener: (context, state) { - Log.d('UserInformationPage: $state'); - }, - child: BlocBuilder(builder: (context, state) { - return _UserInformationContentView(); - }), - ); - } -} class _UserInformationContentView extends StatelessWidget { void _openModifyPage(BuildContext context, ModifyUserInformationType type) { Log.d('_openModifyPage($type)'); - // 暂时关闭修改,修复后打开 - // ModifyUserInformationPage.push(context, type); + ModifyUserInformationPage.push(context, type); } @override @@ -68,7 +53,9 @@ class _UserInformationContentView extends StatelessWidget { radius: 21.r, backgroundImage: ImageUtil.getImageProviderOnDefault(user.avatarUrl), ), - )), + ), + onTap: () => pushNamed(AppRouteName.userAvatar) + ), 11.verticalSpace, _buildContentRow( '名字', @@ -144,9 +131,9 @@ class _UserInformationContentView extends StatelessWidget { 32.horizontalSpace, Expanded( child: Container( - alignment: Alignment.centerLeft, - child: contentWidget, - )), + alignment: Alignment.centerLeft, + child: contentWidget, + )), Offstage( offstage: isHideEndIcon, child: Image.asset(AssetsConst.icNext, width: 20.w, height: 25.h), diff --git a/lib/pages/user/modify/modify_user_avatar_page.dart b/lib/pages/user/modify/modify_user_avatar_page.dart index e69de29..25c5823 100644 --- a/lib/pages/user/modify/modify_user_avatar_page.dart +++ b/lib/pages/user/modify/modify_user_avatar_page.dart @@ -0,0 +1,205 @@ +import 'dart:io'; + +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/common/widgets/ow_image_widget.dart'; +import 'package:wow_english/common/widgets/we_app_bar.dart'; +import 'package:wow_english/pages/user/bloc/user_bloc.dart'; +import 'package:wow_english/pages/user/modify/user_avatar_bloc/user_avatar_bloc.dart'; +import 'package:wow_english/utils/toast_util.dart'; + +class ModifyUserAvatarPage extends StatelessWidget { + const ModifyUserAvatarPage({super.key, required this.pageType}); + + /// 0 新用户设置头像 1 修改头像 + final int pageType; + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (context) => UserAvatarBloc(), + child: _ModifyUserAvatarPage(pageType: pageType), + ); + } +} + +class _ModifyUserAvatarPage extends StatelessWidget { + const _ModifyUserAvatarPage({required this.pageType}); + final int pageType; + + @override + Widget build(BuildContext context) { + return MultiBlocListener( + listeners: [ + BlocListener( + listener: (context, state){ + if (state is UserInfoUpdated) { + showToast('修改成功'); + } + }), + BlocListener( + listener: (context,state){ + if (state is ChangeImageState) { + showToast('上传图片'); + } + },) + ], + child: pageType == 0 ? _setUserAvatarView():_modifyUserAvatarPageView(), + ); + } + + ///个人信息修改头像 + Widget _modifyUserAvatarPageView() => BlocBuilder( + builder: (context,state){ + return Scaffold( + appBar:const WEAppBar( + titleText: '头像修改', + ), + body: SafeArea( + child: Column( + children: [ + 20.verticalSpace, + _avatarImageWidget(), + ], + ), + ), + ); + }); + + ///新用户设置头像 + Widget _setUserAvatarView() => BlocBuilder( + builder: (context, state) { + final bloc = BlocProvider.of(context); + return Container( + color: Colors.white, + child: SafeArea( + child: Column( + children: [ + 20.verticalSpace, + Row( + children: [ + Image.asset( + 'wow_logo'.assetPng, + height: 49.h, + width: 84.w, + ), + Text( + '欢迎登录wow english\n接下来请填写一下您的个人信息吧', + textAlign: TextAlign.left, + style: TextStyle( + fontSize: 17.sp, + color: const Color(0xFF666666) + ), + ) + ], + ), + 15.verticalSpace, + _avatarImageWidget(), + 10.verticalSpace, + Container( + width: 176.w, + height: 52.h, + padding: const EdgeInsets.all(5), + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage(bloc.canInsertApp?'oninto'.assetPng:'intowow'.assetPng), + fit: BoxFit.fill + ) + ), + alignment: Alignment.center, + child: Text( + '进入wow english', + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 12.sp, + color: Colors.white + ), + ), + ) + ], + ), + ), + ); + } + ); + + Widget _avatarImageWidget() => BlocBuilder( + builder: (context, state){ + final bloc = BlocProvider.of(context); + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + width: 205.w, + height: 197.h, + padding: const EdgeInsets.all(5), + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage('video_background'.assetPng), + fit: BoxFit.fill + ) + ), + child: bloc.file == null ? + OwImageWidget(name: bloc.imageUrl): + Image.file( + File(bloc.file!.path), + fit: BoxFit.fill, + ), + ), + 54.horizontalSpace, + Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + GestureDetector( + onTap: () => bloc.add(GetImageFromCameraEvent()), + child: Container( + width: 222.w, + height: 65.h, + alignment: Alignment.center, + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage('alter'.assetPng), + fit: BoxFit.fill + ) + ), + child: Text( + '拍照修改头像', + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.white, + fontSize: 20.sp + ), + ), + ), + ), + 20.verticalSpace, + GestureDetector( + onTap: () => bloc.add(GetImageFromPhotoEvent()), + child: Container( + width: 222.w, + height: 65.h, + alignment: Alignment.center, + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage('alter'.assetPng), + fit: BoxFit.fill + ) + ), + child: Text( + '从相册选择', + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.white, + fontSize: 20.sp + ), + ), + ), + ) + ], + ) + ], + ); + }); +} \ No newline at end of file diff --git a/lib/pages/user/modify/modify_user_information_page.dart b/lib/pages/user/modify/modify_user_information_page.dart index 277aa35..599e3d8 100644 --- a/lib/pages/user/modify/modify_user_information_page.dart +++ b/lib/pages/user/modify/modify_user_information_page.dart @@ -8,7 +8,9 @@ import 'package:wow_english/common/core/user_util.dart'; import 'package:wow_english/common/widgets/textfield_customer_widget.dart'; import 'package:wow_english/common/widgets/we_app_bar.dart'; import 'package:wow_english/pages/user/bloc/user_bloc.dart'; -import 'package:wow_english/utils/log_util.dart'; +import 'package:wow_english/pages/user/modify/user_info_bloc/user_info_bloc.dart'; +import 'package:wow_english/route/route.dart'; +import 'package:wow_english/utils/toast_util.dart'; enum ModifyUserInformationType { avatar('头像'), @@ -34,97 +36,127 @@ class ModifyUserInformationPage extends StatelessWidget { @override Widget build(BuildContext context) { + String text = ''; + if (type == ModifyUserInformationType.age) { + text = UserUtil.getUser()!.age.toString(); + } else if (type == ModifyUserInformationType.name) { + text = UserUtil.getUser()!.name; + } return BlocProvider( - create: (context) => UserBloc(), - child: Scaffold( - backgroundColor: Colors.white, - appBar: WEAppBar( - titleText: '修改${type.title}', - ), - body: BlocListener( - listener: (context, state) {}, - child: BlocBuilder(builder: (context, state) { - //final bloc = context.read(); - // 区别是什么? - //BlocProvider.of(context); - return SingleChildScrollView( - child: Column( - children: [ - Padding( - padding: EdgeInsets.only(left: 65.w, right: 55.w, top: 38.h), - child: Row( + create: (context) => UserInfoBloc()..add(InitControllerTextEvent(text)), + child: MultiBlocListener( + listeners: [ + BlocListener(listener: (context, state){ + if (state is UserInfoUpdated) { + showToast('修改成功'); + popPage(); + } + }) + ], + child: Scaffold( + backgroundColor: Colors.white, + appBar: WEAppBar( + titleText: '修改${type.title}', + ), + body: BlocBuilder( + builder: (context, state) { + final bloc = BlocProvider.of(context); + return SingleChildScrollView( + child: Column( children: [ - Text( - type.title, - style: TextStyle( - fontWeight: FontWeight.w700, - color: const Color(0xFF333333), - fontSize: 21.sp, - ), - ), - 20.horizontalSpace, - // 输入框 or 选择框 - _buildTextFieldWidget(context), - // 占位 - Expanded( - child: Container(), - ), - // 按钮 - GestureDetector( - onTap: () { - // 更新type类型的字段 - context.read().add(UserUpdate(type)); - }, - child: Container( - alignment: Alignment.center, - width: 90.w, - height: 44.h, - decoration: const BoxDecoration( - image: DecorationImage(image: AssetImage(AssetsConst.bgButtonBlue), fit: BoxFit.fill), + Padding( + padding: EdgeInsets.only(left: 65.w, right: 55.w, top: 38.h), + child: Row( + children: [ + Text( + type.title, + style: TextStyle( + fontWeight: FontWeight.w700, + color: const Color(0xFF333333), + fontSize: 21.sp, + ), + ), + 20.horizontalSpace, + // 输入框 or 选择框 + _buildTextFieldWidget(context), + // 占位 + Expanded( + child: Container(), + ), + // 按钮 + GestureDetector( + onTap: () { + var text = ''; + if (type == ModifyUserInformationType.name || type == ModifyUserInformationType.age){ + if(bloc.modifyTextController.text.isEmpty) { + showToast('内容不能为空'); + return; + } + text = bloc.modifyTextController.text; + } + + if (type == ModifyUserInformationType.gender) { + text = bloc.gender.toString(); + } + // 更新type类型的字段 + context.read().add(UserUpdate(type,text)); + }, + child: Container( + alignment: Alignment.center, + width: 90.w, + height: 44.h, + decoration: const BoxDecoration( + image: DecorationImage(image: AssetImage(AssetsConst.bgButtonBlue), fit: BoxFit.fill), + ), + child: Text( + '确定', + style: TextStyle(fontSize: 17.sp, color: Colors.white), + ), + ), + ) + ], + )), + Stack( + alignment: Alignment.topRight, + children: [ + Image.asset( + AssetsConst.bgEditUserInformation, + width: double.infinity, ), - child: Text( - '确定', - style: TextStyle(fontSize: 17.sp, color: Colors.white), + Positioned( + right: 125.w, + top: 10.h, + child: Image.asset(AssetsConst.bgIcSteveWrite, width: 161.w, height: 249.w), ), - ), - ) + ], + ), ], - )), - Stack( - alignment: Alignment.topRight, - children: [ - Image.asset( - AssetsConst.bgEditUserInformation, - width: double.infinity, - ), - Positioned( - right: 125.w, - top: 10.h, - child: Image.asset(AssetsConst.bgIcSteveWrite, width: 161.w, height: 249.w), - ), - ], - ), - ], - )); - }), + )); + }), ), ), ); } Widget _buildTextFieldWidget(BuildContext context) { - var userBloc = context.read(); - var user = UserUtil.getUser()!; - userBloc.tempGender = user.gender ?? 0; - Log.d('user = $user'); if (type == ModifyUserInformationType.gender) { - return Row( - children: [ - _buildSexWidget('男', true), - 70.horizontalSpace, - _buildSexWidget('女', false) - ], - ); + return BlocBuilder( + builder: (context,state){ + final bloc = BlocProvider.of(context); + return Row( + children: [ + GestureDetector( + onTap: () => bloc.add(ChangeGenderEvent(0)), + child: _buildSexWidget('男', bloc.gender == 0), + ), + 70.horizontalSpace, + GestureDetector( + onTap: () => bloc.add(ChangeGenderEvent(1)), + child: _buildSexWidget('女', bloc.gender == 1), + ) + ], + ); + }); } var formatters = [ @@ -141,22 +173,26 @@ class ModifyUserInformationPage extends StatelessWidget { inputType = TextInputType.number; } - return TextFieldCustomerWidget( - height: 65.h, - width: 222.w, - textStyle: TextStyle( - fontWeight: FontWeight.w700, - color: const Color(0xFF333333), - fontSize: 21.sp, - ), - bgImageName: 'Input_layer_up', - textInputType: inputType, - inputFormatters: formatters, - controller: userBloc.modifyTextController, - /*onChangeValue: (String value) { + return BlocBuilder( + builder: (context,state){ + final bloc = BlocProvider.of(context); + return TextFieldCustomerWidget( + height: 65.h, + width: 222.w, + textStyle: TextStyle( + fontWeight: FontWeight.w700, + color: const Color(0xFF333333), + fontSize: 21.sp, + ), + bgImageName: 'Input_layer_up', + textInputType: inputType, + inputFormatters: formatters, + controller: bloc.modifyTextController, + /*onChangeValue: (String value) { bloc.add(CodeNumberChangeEvent()); },*/ - ); + ); + }); } Widget _buildSexWidget(String title,bool isSelect) { diff --git a/lib/pages/user/modify/user_avatar_bloc/user_avatar_bloc.dart b/lib/pages/user/modify/user_avatar_bloc/user_avatar_bloc.dart new file mode 100644 index 0000000..2bf6de8 --- /dev/null +++ b/lib/pages/user/modify/user_avatar_bloc/user_avatar_bloc.dart @@ -0,0 +1,105 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:image_picker/image_picker.dart'; +import 'package:permission_handler/permission_handler.dart'; +import 'package:wow_english/common/core/assets_const.dart'; +import 'package:wow_english/common/core/user_util.dart'; + +part 'user_avatar_event.dart'; +part 'user_avatar_state.dart'; + +class UserAvatarBloc extends Bloc { + + String _imageUrl = UserUtil.getUser()!.avatarUrl??AssetsConst.wowLogo; + + String get imageUrl => _imageUrl; + + XFile? _file; + + XFile? get file => _file; + + bool _canInsertApp =false; + + bool get canInsertApp => _canInsertApp; + + final ImagePicker picker = ImagePicker(); + + + UserAvatarBloc() : super(UserAvatarInitial()) { + on(_changeImage); + on(_getImageFromPhoto); + on(_getImageFromCamera); + } + + void _changeImage(ChangeImageEvent event,Emitter emitter) async { + _imageUrl = event.imagePath; + emitter(ChangeImageState()); + } + + void _getImageFromPhoto(GetImageFromPhotoEvent event,Emitter emitter) async { + await getPhotoPermissionStatus().then((value) async { + if (!value){ + debugPrint('失败$value'); + return; + } + _file = await picker.pickImage(source: ImageSource.gallery); + emitter(ChangeImageState()); + }); + } + + void _getImageFromCamera(GetImageFromCameraEvent event,Emitter emitter) async { + await getCameraPermissionStatus().then((value) async { + if (!value){ + debugPrint('失败$value'); + return; + } + _file = await picker.pickImage(source: ImageSource.camera); + emitter(ChangeImageState()); + }); + } + + ///获取相机权限 + Future getCameraPermissionStatus() async { + Permission permission = Permission.camera; + //granted 通过,denied 被拒绝,permanentlyDenied 拒绝且不在提示 + PermissionStatus status = await permission.status; + if (status.isGranted) { + return true; + } else if (status.isDenied) { + _requestPermission(permission); + } else if (status.isPermanentlyDenied) { + openAppSettings(); + } else if (status.isRestricted) { + _requestPermission(permission); + } else { + + } + return false; + } + + ///获取相册权限 + Future getPhotoPermissionStatus() async { + Permission permission = Permission.photos; + PermissionStatus status = await permission.status; + if (status.isGranted) { + return true; + } else if (status.isDenied) { + _requestPermission(permission); + } else if (status.isPermanentlyDenied) { + openAppSettings(); + } else if (status.isRestricted) { + _requestPermission(permission); + } else { + + } + return false; + } + + /// 获取权限 + void _requestPermission(Permission permission) async { + PermissionStatus status = await permission.request(); + if (status.isPermanentlyDenied) { + openAppSettings(); + } + } +} diff --git a/lib/pages/user/modify/user_avatar_bloc/user_avatar_event.dart b/lib/pages/user/modify/user_avatar_bloc/user_avatar_event.dart new file mode 100644 index 0000000..1a7c299 --- /dev/null +++ b/lib/pages/user/modify/user_avatar_bloc/user_avatar_event.dart @@ -0,0 +1,13 @@ +part of 'user_avatar_bloc.dart'; + +@immutable +abstract class UserAvatarEvent {} + +class GetImageFromPhotoEvent extends UserAvatarEvent {} + +class GetImageFromCameraEvent extends UserAvatarEvent {} + +class ChangeImageEvent extends UserAvatarEvent { + final String imagePath; + ChangeImageEvent(this.imagePath); +} diff --git a/lib/pages/user/modify/user_avatar_bloc/user_avatar_state.dart b/lib/pages/user/modify/user_avatar_bloc/user_avatar_state.dart new file mode 100644 index 0000000..16bf433 --- /dev/null +++ b/lib/pages/user/modify/user_avatar_bloc/user_avatar_state.dart @@ -0,0 +1,8 @@ +part of 'user_avatar_bloc.dart'; + +@immutable +abstract class UserAvatarState {} + +class UserAvatarInitial extends UserAvatarState {} + +class ChangeImageState extends UserAvatarState {} diff --git a/lib/pages/user/modify/user_info_bloc/user_info_bloc.dart b/lib/pages/user/modify/user_info_bloc/user_info_bloc.dart new file mode 100644 index 0000000..2427120 --- /dev/null +++ b/lib/pages/user/modify/user_info_bloc/user_info_bloc.dart @@ -0,0 +1,28 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:wow_english/common/core/user_util.dart'; + +part 'user_info_event.dart'; +part 'user_info_state.dart'; + +class UserInfoBloc extends Bloc { + final TextEditingController modifyTextController = TextEditingController(); + + int _gender = UserUtil.getUser()!.gender??0; + + int get gender => _gender; + + UserInfoBloc() : super(UserInfoInitial()) { + on(_changeGender); + on(_initControllerText); + } + + _initControllerText(InitControllerTextEvent event,Emitter emitter) async { + modifyTextController.text = event.text; + } + + _changeGender(ChangeGenderEvent event,Emitter emitter) async { + _gender = event.gender; + emitter(ChangeGenderState()); + } +} diff --git a/lib/pages/user/modify/user_info_bloc/user_info_event.dart b/lib/pages/user/modify/user_info_bloc/user_info_event.dart new file mode 100644 index 0000000..92c3ebe --- /dev/null +++ b/lib/pages/user/modify/user_info_bloc/user_info_event.dart @@ -0,0 +1,14 @@ +part of 'user_info_bloc.dart'; + +@immutable +abstract class UserInfoEvent {} + +class InitControllerTextEvent extends UserInfoEvent { + final String text; + InitControllerTextEvent(this.text); +} + +class ChangeGenderEvent extends UserInfoEvent { + final int gender; + ChangeGenderEvent(this.gender); +} diff --git a/lib/pages/user/modify/user_info_bloc/user_info_state.dart b/lib/pages/user/modify/user_info_bloc/user_info_state.dart new file mode 100644 index 0000000..615d9ac --- /dev/null +++ b/lib/pages/user/modify/user_info_bloc/user_info_state.dart @@ -0,0 +1,8 @@ +part of 'user_info_bloc.dart'; + +@immutable +abstract class UserInfoState {} + +class UserInfoInitial extends UserInfoState {} + +class ChangeGenderState extends UserInfoState {} diff --git a/lib/pages/user/user_page.dart b/lib/pages/user/user_page.dart index 5b759a2..6ca22e8 100644 --- a/lib/pages/user/user_page.dart +++ b/lib/pages/user/user_page.dart @@ -16,10 +16,7 @@ class UserPage extends StatelessWidget { @override Widget build(BuildContext context) { - return BlocProvider( - create: (context) => UserBloc(), - child: _UserView(), - ); + return _UserView(); } } @@ -32,38 +29,37 @@ class _UserView extends StatelessWidget { } Widget _pageWidget() => BlocBuilder( - /*buildWhen: (previous, current) { + /*buildWhen: (previous, current) { return current != previous; },*/ - builder: (context, state) { - UserEntity user = UserUtil.getUser()!; - final userBloc = BlocProvider.of(context); + builder: (context, state) { + UserEntity user = UserUtil.getUser()!; + final userBloc = BlocProvider.of(context); - // 常规按钮的字体样式 - final textStyle21sp = TextStyle( - //fontWeight: FontWeight.w600, - color: const Color(0xFF333333), - fontSize: 21.sp, - ); + // 常规按钮的字体样式 + final textStyle21sp = TextStyle( + //fontWeight: FontWeight.w600, + color: const Color(0xFF333333), + fontSize: 21.sp, + ); - // 常规按钮的样式 - var normalButtonStyle = ButtonStyle( - side: MaterialStateProperty.all(BorderSide(color: const Color(0xFF140C10), width: 1.5.w)), - shape: MaterialStateProperty.all(RoundedRectangleBorder(borderRadius: BorderRadius.circular(15.r))), - minimumSize: MaterialStateProperty.all(Size(double.infinity, 58.h)), - backgroundColor: MaterialStateProperty.all(Colors.white), - ); + // 常规按钮的样式 + var normalButtonStyle = ButtonStyle( + side: MaterialStateProperty.all(BorderSide(color: const Color(0xFF140C10), width: 1.5.w)), + shape: MaterialStateProperty.all(RoundedRectangleBorder(borderRadius: BorderRadius.circular(15.r))), + minimumSize: MaterialStateProperty.all(Size(double.infinity, 58.h)), + backgroundColor: MaterialStateProperty.all(Colors.white), + ); - return Scaffold( - //backgroundColor: Colors.white, - appBar: const WEAppBar(), - body: SingleChildScrollView( - padding: EdgeInsets.only(left: 17.w, right: 17.w, top: 10.h, bottom: 22.h), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - // todo banner,暂时没有接口获取banner URL - /*Offstage( + return Scaffold( + appBar: const WEAppBar(), + body: SingleChildScrollView( + padding: EdgeInsets.only(left: 17.w, right: 17.w, top: 10.h, bottom: 22.h), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + // todo banner,暂时没有接口获取banner URL + /*Offstage( child: Column( children: [ Container(child: Image.asset(bannerUrl), constraints: BoxConstraints(maxHeight: 196.h)), @@ -71,23 +67,23 @@ class _UserView extends StatelessWidget { ], ), ),*/ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - CircleAvatar( - radius: 40.r, - backgroundColor: const Color(0xFF140C10), - child: CircleAvatar( - radius: 38.5.r, - backgroundImage: ImageUtil.getImageProviderOnDefault(user.avatarUrl), - ), - /*child: ClipOval( + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + CircleAvatar( + radius: 40.r, + backgroundColor: const Color(0xFF140C10), + child: CircleAvatar( + radius: 38.5.r, + backgroundImage: ImageUtil.getImageProviderOnDefault(user.avatarUrl), + ), + /*child: ClipOval( child: OwImageWidget(name: user.avatarUrl ?? AssetsConst.wowLogo, fit: BoxFit.contain,), )*/ - ), - 32.horizontalSpace, - Expanded( - child: Column( + ), + 32.horizontalSpace, + Expanded( + child: Column( children: [ Row( children: [ @@ -131,94 +127,94 @@ class _UserView extends StatelessWidget { ) ], )), - Offstage( - offstage: UserUtil.getUser()?.phoneNum == '17730280759', - child: TextButton( - child: Text( - "修改个人信息>", - style: textStyle21sp, - ), - onPressed: () { - pushNamed(AppRouteName.userInformation); - }, - ), - ) - ], - ), - 30.verticalSpace, - OutlinedButton( - onPressed: () => pushNamed(AppRouteName.fogPwd), - style: normalButtonStyle, - child: Text( - "修改密码", - style: textStyle21sp, - ), - ), - 12.verticalSpace, - // todo 为了过审,把测试账号兑换功能下掉 - Offstage( - offstage: UserUtil.getUser()?.phoneNum == '17730280759', - child: OutlinedButton( - onPressed: () => pushNamed(AppRouteName.exLesson), - style: normalButtonStyle, - child: Text( - "兑换课程", - style: textStyle21sp, - )), - ), Offstage( offstage: UserUtil.getUser()?.phoneNum == '17730280759', - child: 12.verticalSpace, - ), - OutlinedButton( - onPressed: () { - pushNamed(AppRouteName.webView,arguments: {'urlStr': AppConsts.userPrivacyPolicyUrl, 'webViewTitle': '隐私协议'}); - }, - style: normalButtonStyle, + child: TextButton( child: Text( - "隐私协议", + "修改个人信息>", style: textStyle21sp, - )), - 30.verticalSpace, - OutlinedButton( - onPressed: () => userBloc.add(UserLogout()), - style: ButtonStyle( - side: MaterialStateProperty.all(const BorderSide(color: Color(0xFF140C10), width: 1.5)), - shape: MaterialStateProperty.all( - RoundedRectangleBorder(borderRadius: BorderRadius.circular(15.r))), - minimumSize: MaterialStateProperty.all(Size(295.w, 40.h)), - backgroundColor: MaterialStateProperty.all(Color(0xFFFBB621)), ), - child: Text( - "退出登录", - style: TextStyle( - //fontWeight: FontWeight.w600, - color: Colors.white, - fontSize: 17.sp, - ), - )), - 30.verticalSpace, - TextButton( onPressed: () { - //userBloc.add(UserDelete()) - showTwoActionDialog('注销账号', '取消', '注销', '请谨慎操作!\n注销后不可恢复哦!', () { - popPage(); - }, () { - userBloc.add(UserDelete()); - popPage(); - }); + pushNamed(AppRouteName.userInformation); }, - child: Text( - "注销账号", - style: TextStyle( - //fontWeight: FontWeight.w600, - color: Colors.red, - fontSize: 15.sp, - ), - )), + ), + ) ], ), - )); - }, - ); + 30.verticalSpace, + OutlinedButton( + onPressed: () => pushNamed(AppRouteName.fogPwd), + style: normalButtonStyle, + child: Text( + "修改密码", + style: textStyle21sp, + ), + ), + 12.verticalSpace, + // todo 为了过审,把测试账号兑换功能下掉 + Offstage( + offstage: UserUtil.getUser()?.phoneNum == '17730280759', + child: OutlinedButton( + onPressed: () => pushNamed(AppRouteName.exLesson), + style: normalButtonStyle, + child: Text( + "兑换课程", + style: textStyle21sp, + )), + ), + Offstage( + offstage: UserUtil.getUser()?.phoneNum == '17730280759', + child: 12.verticalSpace, + ), + OutlinedButton( + onPressed: () { + pushNamed(AppRouteName.webView,arguments: {'urlStr': AppConsts.userPrivacyPolicyUrl, 'webViewTitle': '隐私协议'}); + }, + style: normalButtonStyle, + child: Text( + "隐私协议", + style: textStyle21sp, + )), + 30.verticalSpace, + OutlinedButton( + onPressed: () => userBloc.add(UserLogout()), + style: ButtonStyle( + side: MaterialStateProperty.all(const BorderSide(color: Color(0xFF140C10), width: 1.5)), + shape: MaterialStateProperty.all( + RoundedRectangleBorder(borderRadius: BorderRadius.circular(15.r))), + minimumSize: MaterialStateProperty.all(Size(295.w, 40.h)), + backgroundColor: MaterialStateProperty.all(Color(0xFFFBB621)), + ), + child: Text( + "退出登录", + style: TextStyle( + //fontWeight: FontWeight.w600, + color: Colors.white, + fontSize: 17.sp, + ), + )), + 30.verticalSpace, + TextButton( + onPressed: () { + //userBloc.add(UserDelete()) + showTwoActionDialog('注销账号', '取消', '注销', '请谨慎操作!\n注销后不可恢复哦!', () { + popPage(); + }, () { + userBloc.add(UserDelete()); + popPage(); + }); + }, + child: Text( + "注销账号", + style: TextStyle( + //fontWeight: FontWeight.w600, + color: Colors.red, + fontSize: 15.sp, + ), + )), + ], + ), + )); + }, + ); } diff --git a/lib/route/route.dart b/lib/route/route.dart index 9769771..935e0f8 100644 --- a/lib/route/route.dart +++ b/lib/route/route.dart @@ -16,6 +16,7 @@ import 'package:wow_english/pages/shop/exchangelist/exchange_lesson_list_page.da import 'package:wow_english/pages/shop/home/shop_home_page.dart'; import 'package:wow_english/pages/tab/tab_page.dart'; import 'package:wow_english/pages/user/information/user_information_page.dart'; +import 'package:wow_english/pages/user/modify/modify_user_avatar_page.dart'; import 'package:wow_english/pages/user/modify/modify_user_information_page.dart'; import 'package:wow_english/pages/user/user_page.dart'; import 'package:wow_english/pages/video/lookvideo/look_video_page.dart'; @@ -42,6 +43,8 @@ class AppRouteName { /// 用户详细信息页 static const String userInformation = 'userInformation'; + /// 修改用户头像 + static const String userAvatar = 'userAvatar'; /// 用户修改信息页,不要自己调用,使用[ModifyUserInformationPage.push]方法,隐藏这种实现 //static const String userModifyInformation = 'userModifyInformation'; @@ -99,6 +102,13 @@ class AppRouter { return CupertinoPageRoute(builder: (_) => const UserPage()); case AppRouteName.userInformation: return CupertinoPageRoute(builder: (_) => const UserInformationPage()); + case AppRouteName.userAvatar: + var pageType = 0; + if (settings.arguments != null) { + final content = (settings.arguments as Map)['pageType'] as String??'0'; + pageType = int.parse(content); + } + return CupertinoPageRoute(builder: (_) => ModifyUserAvatarPage(pageType: pageType,)); /*case AppRouteName.userModifyInformation: return CupertinoPageRoute(builder: (_) { ModifyUserInformationType argument = ModifyUserInformationType.name; diff --git a/pubspec.yaml b/pubspec.yaml index a46c977..a2df7f6 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -80,7 +80,7 @@ dependencies: # 获取设备信息 https://pub.flutter-io.cn/packages/device_info_plus device_info_plus: ^9.0.1 # 用户权限申请 https://pub.dev/packages/permission_handler - permission_handler: ^10.2.0 + permission_handler: ^10.4.1 # 网络状态监听 https://pub.dev/packages/connectivity_plus connectivity_plus: ^4.0.1 # iOS设备方向控制 https://pub.dev/packages/limiting_direction_csx