You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
332 lines
13 KiB
332 lines
13 KiB
import 'package:auto_size_text/auto_size_text.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
import 'package:go_router/go_router.dart';
|
|
import 'package:hadi_hoda_flutter/common_ui/resources/my_assets.dart';
|
|
import 'package:hadi_hoda_flutter/common_ui/resources/my_spaces.dart';
|
|
import 'package:hadi_hoda_flutter/common_ui/resources/my_text_style.dart';
|
|
import 'package:hadi_hoda_flutter/core/constants/my_constants.dart';
|
|
import 'package:hadi_hoda_flutter/core/routers/my_routes.dart';
|
|
import 'package:hadi_hoda_flutter/core/status/base_status.dart';
|
|
import 'package:hadi_hoda_flutter/core/utils/my_localization.dart';
|
|
import 'package:hadi_hoda_flutter/core/utils/screen_size.dart';
|
|
import 'package:hadi_hoda_flutter/core/utils/set_platform_size.dart';
|
|
import 'package:hadi_hoda_flutter/core/widgets/button/my_blue_button.dart';
|
|
import 'package:hadi_hoda_flutter/core/widgets/error/error_state.dart';
|
|
import 'package:hadi_hoda_flutter/core/widgets/images/my_image.dart';
|
|
import 'package:hadi_hoda_flutter/features/app/presentation/bloc/app_bloc.dart';
|
|
import 'package:hadi_hoda_flutter/features/app/presentation/bloc/app_event.dart';
|
|
import 'package:hadi_hoda_flutter/features/download/domain/entities/download_entity.dart';
|
|
import 'package:hadi_hoda_flutter/features/download/presentation/bloc/download_bloc.dart';
|
|
import 'package:hadi_hoda_flutter/features/download/presentation/bloc/download_event.dart';
|
|
import 'package:hadi_hoda_flutter/features/language/presentation/bloc/language_bloc.dart';
|
|
import 'package:hadi_hoda_flutter/features/language/presentation/bloc/language_event.dart';
|
|
import 'package:hadi_hoda_flutter/features/language/presentation/bloc/language_state.dart';
|
|
import 'package:wheel_chooser/wheel_chooser.dart';
|
|
|
|
import '../../../../core/widgets/animations/rotation_anim.dart';
|
|
import '../../domain/entity/language_entity.dart';
|
|
|
|
class LanguagePage extends StatefulWidget {
|
|
const LanguagePage({super.key});
|
|
|
|
@override
|
|
State<LanguagePage> createState() => _LanguagePageState();
|
|
}
|
|
|
|
class _LanguagePageState extends State<LanguagePage> {
|
|
final controller = FixedExtentScrollController(initialItem: 50);
|
|
bool _isEnabled = false;
|
|
LanguageEntity? _selectedLanguage;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_selectedLanguage =
|
|
context.read<LanguageBloc>().state.selectedLanguage ??
|
|
const LanguageEntity(
|
|
code: 'en',
|
|
title: '',
|
|
displayName: 'English (English',
|
|
);
|
|
context.read<LanguageBloc>().add(const GetLanguagesEvent());
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
controller.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
body: Container(
|
|
height: context.heightScreen,
|
|
width: context.widthScreen,
|
|
decoration: BoxDecoration(
|
|
gradient: const LinearGradient(
|
|
begin: Alignment.topCenter,
|
|
end: Alignment.bottomCenter,
|
|
colors: [Color(0XFF00154C), Color(0XFF150532)],
|
|
),
|
|
image: DecorationImage(
|
|
image: const AssetImage(MyAssets.pattern),
|
|
scale: 3,
|
|
repeat: ImageRepeat.repeat,
|
|
colorFilter: ColorFilter.mode(
|
|
Colors.white.withValues(alpha: 0.2),
|
|
BlendMode.srcIn,
|
|
),
|
|
),
|
|
),
|
|
child: BlocConsumer<LanguageBloc, LanguageState>(
|
|
listener: (context, state) async {
|
|
if (state.languages.isNotEmpty && !_isEnabled) {
|
|
int targetIndex = 0;
|
|
if (state.selectedLanguage != null) {
|
|
targetIndex = state.languages.indexWhere(
|
|
(l) => l.code == state.selectedLanguage!.code,
|
|
);
|
|
if (targetIndex == -1) targetIndex = 0;
|
|
}
|
|
|
|
await Future.delayed(const Duration(milliseconds: 50));
|
|
if (controller.hasClients) {
|
|
await controller.animateToItem(
|
|
targetIndex,
|
|
duration: const Duration(seconds: 1),
|
|
curve: Curves.easeOutCirc,
|
|
);
|
|
|
|
if (state.selectedLanguage == null && context.mounted) {
|
|
context.read<LanguageBloc>().add(
|
|
SelectLanguageEvent(state.languages[targetIndex]),
|
|
);
|
|
}
|
|
}
|
|
setState(() => _isEnabled = true);
|
|
}
|
|
},
|
|
builder: (context, state) {
|
|
if (state.getLanguagesStatus is BaseLoading) {
|
|
return Center(child: _loading(context));
|
|
}
|
|
if (state.getLanguagesStatus is BaseError) {
|
|
return Padding(
|
|
padding: EdgeInsets.symmetric(
|
|
vertical:
|
|
MediaQuery.viewPaddingOf(context).bottom + MySpaces.s16,
|
|
horizontal: 60,
|
|
),
|
|
child: ErrorState(
|
|
onTap: () {
|
|
context.read<LanguageBloc>().add(const GetLanguagesEvent());
|
|
},
|
|
),
|
|
);
|
|
}
|
|
|
|
final double itemSize =
|
|
setSize(context: context, mobile: 45, tablet: 60) ?? 45;
|
|
final selectStyle = MYTextStyle.titr1.copyWith(
|
|
// Keep same size as before (WheelChooser.choices)
|
|
fontSize: itemSize * 0.34,
|
|
color: Colors.white,
|
|
);
|
|
final unSelectStyle = MYTextStyle.titr1.copyWith(
|
|
fontSize: itemSize * 0.34,
|
|
color: Colors.white.withValues(alpha: 0.5),
|
|
);
|
|
return Padding(
|
|
padding: EdgeInsets.only(
|
|
left: setSize(context: context, mobile: 50, tablet: 0.3.w) ?? 0,
|
|
right:
|
|
setSize(context: context, mobile: 50, tablet: 0.3.w) ?? 0,
|
|
bottom: MySpaces.s40 + MediaQuery.paddingOf(context).bottom,
|
|
top: 60,
|
|
),
|
|
child: Column(
|
|
children: [
|
|
_title(context),
|
|
const SizedBox(height: 72),
|
|
Expanded(
|
|
child: ShaderMask(
|
|
shaderCallback: (rect) {
|
|
return const LinearGradient(
|
|
begin: Alignment.topCenter,
|
|
end: Alignment.bottomCenter,
|
|
colors: [
|
|
Color(0x00FFF7FA),
|
|
Color(0x1AFFF7FA),
|
|
Color(0xFFFFFFFF),
|
|
Color(0x1AFEFCFD),
|
|
Color(0x00FEFCFD),
|
|
],
|
|
).createShader(rect);
|
|
},
|
|
child: Stack(
|
|
children: [
|
|
Center(
|
|
child: Container(
|
|
width: double.infinity,
|
|
alignment: Alignment.centerLeft,
|
|
height: itemSize,
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 18,
|
|
),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white.withValues(alpha: .2),
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
child: Transform.translate(
|
|
offset:
|
|
(state
|
|
.selectedLanguage
|
|
?.displayName
|
|
.length ??
|
|
0) >
|
|
21
|
|
? const Offset(-10, 0)
|
|
: Offset.zero,
|
|
child: const MyImage(image: MyAssets.check),
|
|
),
|
|
),
|
|
),
|
|
WheelChooser.custom(
|
|
controller: controller,
|
|
datas: null,
|
|
startPosition: null,
|
|
perspective: 0.00000001,
|
|
listWidth: double.infinity,
|
|
listHeight: double.infinity,
|
|
itemSize: itemSize,
|
|
isInfinite: true,
|
|
onValueChanged: (position) {
|
|
if (state.languages.isEmpty) return;
|
|
final pos = position is int
|
|
? position
|
|
: int.tryParse(position.toString()) ?? 0;
|
|
final index =
|
|
((pos % state.languages.length) +
|
|
state.languages.length) %
|
|
state.languages.length;
|
|
final lang = state.languages[index];
|
|
setState(() {
|
|
_selectedLanguage = lang;
|
|
});
|
|
},
|
|
children: state.languages.map((lang) {
|
|
final isSelected =
|
|
_selectedLanguage?.code == lang.code;
|
|
return Center(
|
|
child: Padding(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 36,
|
|
),
|
|
child: MediaQuery(
|
|
data: MediaQuery.of(context).copyWith(
|
|
textScaler: const TextScaler.linear(1.5),
|
|
),
|
|
child: AutoSizeText(
|
|
lang.displayName,
|
|
textAlign: TextAlign.center,
|
|
maxLines: 2,
|
|
minFontSize: 10,
|
|
stepGranularity: 0.5,
|
|
overflow: TextOverflow.ellipsis,
|
|
style: isSelected
|
|
? selectStyle
|
|
: unSelectStyle,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}).toList(),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(height: 72),
|
|
_btn(context, state),
|
|
],
|
|
),
|
|
);
|
|
},
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _loading(BuildContext context) {
|
|
return RotationAnim(
|
|
child: MyImage(
|
|
image: MyAssets.loading,
|
|
size: setSize(context: context, mobile: 110, tablet: 145),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _title(BuildContext context) {
|
|
return Row(
|
|
spacing: MySpaces.s10,
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
const MyImage(image: MyAssets.lang, size: 28),
|
|
AutoSizeText(
|
|
context.translate.select_language,
|
|
minFontSize: 12,
|
|
maxFontSize: 20,
|
|
maxLines: 1,
|
|
textAlign: TextAlign.center,
|
|
style: MYTextStyle.titr0.copyWith(color: const Color(0XFF847AC4)),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _btn(BuildContext context, LanguageState state) {
|
|
return MyBlueButton(
|
|
onTap: (_isEnabled && _selectedLanguage != null)
|
|
? () async {
|
|
context.read<LanguageBloc>().add(
|
|
SelectLanguageEvent(_selectedLanguage!),
|
|
);
|
|
final downloadBloc = context.read<DownloadBloc>();
|
|
// 1. Cancel any previous downloads.
|
|
downloadBloc.add(CancelDownloadEvent());
|
|
|
|
// 2. Update App Locale
|
|
context.read<AppBloc>().add(
|
|
ChangeLocaleEvent(_selectedLanguage!.locale),
|
|
);
|
|
await Future.delayed(const Duration(milliseconds: 80));
|
|
final lastDownloadedLevel = await downloadBloc
|
|
.lastDownloadedLevel();
|
|
|
|
if (!context.mounted) return;
|
|
|
|
if (lastDownloadedLevel >= MyConstants.firstDownloadBatchCount) {
|
|
context.goNamed(Routes.homePage);
|
|
} else {
|
|
context.read<DownloadBloc>().add(
|
|
const StartDownloadEvent(
|
|
toLevel: MyConstants.firstDownloadBatchCount,
|
|
),
|
|
);
|
|
|
|
context.goNamed(
|
|
Routes.downloadPage,
|
|
extra: const DownloadPageConfig(
|
|
downloadToLevel: MyConstants.firstDownloadBatchCount,
|
|
redirectTo: Routes.homePage,
|
|
),
|
|
);
|
|
}
|
|
}
|
|
: null,
|
|
title: context.translate.select,
|
|
);
|
|
}
|
|
}
|