feature(training): Добавлен состояния загрузки, пустоты и наличие данных на экране тренировки

This commit is contained in:
2025-06-17 22:27:22 +03:00
parent 0842c479c7
commit 285c4ca3f2
17 changed files with 1098 additions and 65 deletions

View File

@@ -0,0 +1,185 @@
import 'package:auto_route/auto_route.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:remever/common/resources.dart';
import 'package:remever/common/typography.dart';
import 'package:remever/common/widgets/typography.dart';
import 'package:remever/common/widgets/w_if.dart';
import 'package:remever/common/widgets/wspace.dart';
import 'package:remever/components/extensions/context.dart';
import 'package:remever/components/extensions/state.dart';
import 'package:remever/gen/assets.gen.dart';
import 'package:remever/router.gr.dart';
import 'package:remever/screens/training/cubit/training_cubit.dart';
import 'package:remever/screens/training/states/empty.dart';
import 'package:remever/screens/training/states/loading.dart';
import 'package:remever/screens/training/widgets/training_ticket.dart';
import 'package:remever/widgets/primary_button.dart';
@RoutePage()
class TrainingScreen extends StatelessWidget {
const TrainingScreen({super.key});
@override
Widget build(BuildContext context) {
return BlocProvider<TrainingCubit>(
create: (BuildContext context) => TrainingCubit(),
child: _buildMain(),
);
}
///
/// Построение основного блока
///
Widget _buildMain() {
return PopScope(
canPop: false,
child: BlocBuilder<TrainingCubit, TrainingState>(
builder: (BuildContext context, state) {
return state.when(
loading: () => TrainingLoading(),
empty: () => TrainingEmpty(),
data: () => TrainingData(),
result: () => Placeholder(),
);
},
),
);
}
}
class TrainingData extends StatefulWidget {
const TrainingData({super.key});
@override
State<TrainingData> createState() => _TrainingDataState();
}
class _TrainingDataState extends State<TrainingData> {
bool _showAnswer = false;
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColors.bg,
appBar: _buildAppBar(context),
body: _buildMain(),
);
}
/// Построение шапки
AppBar _buildAppBar(BuildContext context) {
return AppBar(
backgroundColor: AppColors.white,
shadowColor: Colors.transparent,
leading: GestureDetector(
onTap: () => context.back(),
child: const Icon(Icons.close, color: Colors.black),
),
centerTitle: true,
title: AppTypography(
'Тренировка',
type: SemiBold20px(),
color: AppColors.body_text,
),
actions: [_buildCounter(), WSpace(16)],
);
}
Widget _buildCounter() {
return Center(
child: Container(
height: 24.h,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(100)).r,
color: AppColors.bg,
),
child: Padding(
padding: const EdgeInsets.all(3).r,
child: Center(child: AppTypography('1 из 9', type: Regular14px())),
),
),
);
}
Widget _buildMain() {
return Column(
children: [_buildTickets(), _buildShowAnswerButton(), _buildActions()],
);
}
Widget _buildActions() {
return Padding(
padding: const EdgeInsets.all(16).r,
child: Row(
spacing: 8.r,
children: [
Flexible(
child: PrimaryButton(
onTap: () {},
color: AppColors.danger,
child: AppTypography(
'Не помню',
type: Medium14px(),
color: Colors.white,
),
),
),
Flexible(
child: PrimaryButton(
child: AppTypography(
'Помню',
type: Medium14px(),
color: Colors.white,
),
onTap: () {},
),
),
],
),
);
}
Widget _buildShowAnswerButton() {
return Wif(
condition: !_showAnswer,
builder:
(context) => PrimaryButton(
onTap: () {
safeSetState(() {
_showAnswer = true;
});
},
color: AppColors.secondary,
width: 170,
child: AppTypography(
'Показать ответ',
type: Medium14px(),
color: AppColors.primary,
),
),
);
}
Widget _buildTickets() {
return Expanded(
child: SingleChildScrollView(
physics: BouncingScrollPhysics(),
child: Padding(
padding: const EdgeInsets.all(16).r,
child: Column(
spacing: 8.r,
children: [
TrainingTicket(),
Wif(
condition: _showAnswer,
builder: (context) => TrainingTicket(isAnswer: true),
),
],
),
),
),
);
}
}