ыегаа
This commit is contained in:
parent
fc99d384bc
commit
775ba356ef
25 changed files with 585 additions and 247 deletions
Binary file not shown.
BIN
icons/gift.png
Normal file
BIN
icons/gift.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 684 B |
BIN
icons/next.png
Normal file
BIN
icons/next.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 874 B |
BIN
icons/settings.png
Normal file
BIN
icons/settings.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 847 B |
BIN
icons/tg.png
Normal file
BIN
icons/tg.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3 KiB |
|
|
@ -158,6 +158,15 @@ class EditUser {
|
|||
);
|
||||
}),
|
||||
_Payments(adminApi, userDto),
|
||||
Text(
|
||||
'Stats: ${userDto.userDataDto?.allWordsStatistics?.total} words',
|
||||
),
|
||||
Text(
|
||||
'Stats: ${userDto.userDataDto?.allTestsStatistics?.tests.length} tests',
|
||||
),
|
||||
Text(
|
||||
'Last test session: ${userDto.userDataDto?.allTestsStatistics?.lastSessionToken}',
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import 'package:mnemo_cards/features/packs/packs_api.dart';
|
|||
import 'package:mnemo_cards/managers/repository/api.dart';
|
||||
import 'package:mnemo_cards_common/mnemo_cards_common.dart';
|
||||
|
||||
|
||||
class AdminApi with Api {
|
||||
final Dio _dio;
|
||||
|
||||
|
|
|
|||
|
|
@ -83,19 +83,19 @@ class _AllUsersState extends State<AllUsers> {
|
|||
}
|
||||
|
||||
List<UserDto> buildUsersList() {
|
||||
final filteredCards = users.where((dto) {
|
||||
final filteredUsers = users.where((dto) {
|
||||
final filterOk = filter.isEmpty ||
|
||||
dto.name.toString().toLowerCase().contains(filter) ||
|
||||
dto.id.toString().toLowerCase().contains(filter) ||
|
||||
dto.packs.any((p) => p.toLowerCase().contains(filter));
|
||||
return filterOk;
|
||||
});
|
||||
return filteredCards.toList();
|
||||
return filteredUsers.toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final filteredCards = buildUsersList();
|
||||
final filteredUsers = buildUsersList();
|
||||
return Scaffold(
|
||||
body: SafeArea(
|
||||
child: Stack(
|
||||
|
|
@ -110,9 +110,9 @@ class _AllUsersState extends State<AllUsers> {
|
|||
_currentId == null
|
||||
? 'Все'
|
||||
: _currentUser?.name ?? 'Пак ${_currentUser?.id}',
|
||||
subtitle: filteredCards.isEmpty
|
||||
subtitle: filteredUsers.isEmpty
|
||||
? 'Нет пользователей'
|
||||
: '${filteredCards.length} пользователя',
|
||||
: '${filteredUsers.length} пользователя',
|
||||
hasPopButton: true,
|
||||
trail: IconButton(
|
||||
onPressed: () async {
|
||||
|
|
@ -148,8 +148,8 @@ class _AllUsersState extends State<AllUsers> {
|
|||
],
|
||||
),
|
||||
);
|
||||
else if (filteredCards.length > index - 1)
|
||||
return userWidget(filteredCards[index - 1]);
|
||||
else if (filteredUsers.length > index - 1)
|
||||
return userWidget(filteredUsers[index - 1]);
|
||||
else
|
||||
return null;
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -31,6 +31,11 @@ class AppRouter extends $AppRouter {
|
|||
transitionsBuilder: TransitionsBuilders.slideLeftWithFade,
|
||||
durationInMilliseconds: 200,
|
||||
),
|
||||
CustomRoute(
|
||||
page: SettingsPage.page,
|
||||
transitionsBuilder: TransitionsBuilders.slideLeftWithFade,
|
||||
durationInMilliseconds: 200,
|
||||
),
|
||||
CustomRoute(
|
||||
// initial: !locator.userManager.hasUser,
|
||||
page: AuthPage.page,
|
||||
|
|
|
|||
|
|
@ -8,33 +8,34 @@
|
|||
// coverage:ignore-file
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'dart:ui' as _i10;
|
||||
import 'dart:ui' as _i11;
|
||||
|
||||
import 'package:auto_route/auto_route.dart' as _i8;
|
||||
import 'package:flutter/material.dart' as _i11;
|
||||
import 'package:mnemo_cards/features/tests/test_page.dart' as _i7;
|
||||
import 'package:auto_route/auto_route.dart' as _i9;
|
||||
import 'package:flutter/material.dart' as _i12;
|
||||
import 'package:mnemo_cards/features/tests/test_page.dart' as _i8;
|
||||
import 'package:mnemo_cards/main.dart' as _i5;
|
||||
import 'package:mnemo_cards/pages/auth_page.dart' as _i1;
|
||||
import 'package:mnemo_cards/pages/home_page.dart' as _i4;
|
||||
import 'package:mnemo_cards/pages/profile_page.dart' as _i6;
|
||||
import 'package:mnemo_cards/pages/settgins_page.dart' as _i7;
|
||||
import 'package:mnemo_cards/widgets/card_game/card_game_page.dart' as _i2;
|
||||
import 'package:mnemo_cards/widgets/card_pack/card_pack_page.dart' as _i3;
|
||||
import 'package:mnemo_cards_common/mnemo_cards_common.dart' as _i9;
|
||||
import 'package:mnemo_cards_common/mnemo_cards_common.dart' as _i10;
|
||||
|
||||
abstract class $AppRouter extends _i8.RootStackRouter {
|
||||
abstract class $AppRouter extends _i9.RootStackRouter {
|
||||
$AppRouter({super.navigatorKey});
|
||||
|
||||
@override
|
||||
final Map<String, _i8.PageFactory> pagesMap = {
|
||||
final Map<String, _i9.PageFactory> pagesMap = {
|
||||
AuthPage.name: (routeData) {
|
||||
return _i8.AutoRoutePage<dynamic>(
|
||||
return _i9.AutoRoutePage<dynamic>(
|
||||
routeData: routeData,
|
||||
child: _i1.AuthPage(),
|
||||
);
|
||||
},
|
||||
CardGamePage.name: (routeData) {
|
||||
final args = routeData.argsAs<CardGamePageArgs>();
|
||||
return _i8.AutoRoutePage<dynamic>(
|
||||
return _i9.AutoRoutePage<dynamic>(
|
||||
routeData: routeData,
|
||||
child: _i2.CardGamePage(
|
||||
cards: args.cards,
|
||||
|
|
@ -48,7 +49,7 @@ abstract class $AppRouter extends _i8.RootStackRouter {
|
|||
},
|
||||
CardPackPage.name: (routeData) {
|
||||
final args = routeData.argsAs<CardPackPageArgs>();
|
||||
return _i8.AutoRoutePage<dynamic>(
|
||||
return _i9.AutoRoutePage<dynamic>(
|
||||
routeData: routeData,
|
||||
child: _i3.CardPackPage(
|
||||
args.previewDto,
|
||||
|
|
@ -57,28 +58,34 @@ abstract class $AppRouter extends _i8.RootStackRouter {
|
|||
);
|
||||
},
|
||||
HomePage.name: (routeData) {
|
||||
return _i8.AutoRoutePage<dynamic>(
|
||||
return _i9.AutoRoutePage<dynamic>(
|
||||
routeData: routeData,
|
||||
child: _i4.HomePage(),
|
||||
);
|
||||
},
|
||||
MainTabsPage.name: (routeData) {
|
||||
return _i8.AutoRoutePage<dynamic>(
|
||||
return _i9.AutoRoutePage<dynamic>(
|
||||
routeData: routeData,
|
||||
child: const _i5.MainTabsPage(),
|
||||
);
|
||||
},
|
||||
ProfilePage.name: (routeData) {
|
||||
return _i8.AutoRoutePage<dynamic>(
|
||||
return _i9.AutoRoutePage<dynamic>(
|
||||
routeData: routeData,
|
||||
child: _i6.ProfilePage(),
|
||||
);
|
||||
},
|
||||
SettingsPage.name: (routeData) {
|
||||
return _i9.AutoRoutePage<dynamic>(
|
||||
routeData: routeData,
|
||||
child: _i7.SettingsPage(),
|
||||
);
|
||||
},
|
||||
TestPage.name: (routeData) {
|
||||
final args = routeData.argsAs<TestPageArgs>();
|
||||
return _i8.AutoRoutePage<dynamic>(
|
||||
return _i9.AutoRoutePage<dynamic>(
|
||||
routeData: routeData,
|
||||
child: _i7.TestPage(args.testId),
|
||||
child: _i8.TestPage(args.testId),
|
||||
);
|
||||
},
|
||||
};
|
||||
|
|
@ -86,8 +93,8 @@ abstract class $AppRouter extends _i8.RootStackRouter {
|
|||
|
||||
/// generated route for
|
||||
/// [_i1.AuthPage]
|
||||
class AuthPage extends _i8.PageRouteInfo<void> {
|
||||
const AuthPage({List<_i8.PageRouteInfo>? children})
|
||||
class AuthPage extends _i9.PageRouteInfo<void> {
|
||||
const AuthPage({List<_i9.PageRouteInfo>? children})
|
||||
: super(
|
||||
AuthPage.name,
|
||||
initialChildren: children,
|
||||
|
|
@ -95,20 +102,20 @@ class AuthPage extends _i8.PageRouteInfo<void> {
|
|||
|
||||
static const String name = 'AuthPage';
|
||||
|
||||
static const _i8.PageInfo<void> page = _i8.PageInfo<void>(name);
|
||||
static const _i9.PageInfo<void> page = _i9.PageInfo<void>(name);
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [_i2.CardGamePage]
|
||||
class CardGamePage extends _i8.PageRouteInfo<CardGamePageArgs> {
|
||||
class CardGamePage extends _i9.PageRouteInfo<CardGamePageArgs> {
|
||||
CardGamePage({
|
||||
required List<_i9.GameCardDto> cards,
|
||||
required _i10.Color? color,
|
||||
required List<_i10.GameCardDto> cards,
|
||||
required _i11.Color? color,
|
||||
int? startCardId,
|
||||
bool infinite = false,
|
||||
bool shuffle = true,
|
||||
_i11.Key? key,
|
||||
List<_i8.PageRouteInfo>? children,
|
||||
_i12.Key? key,
|
||||
List<_i9.PageRouteInfo>? children,
|
||||
}) : super(
|
||||
CardGamePage.name,
|
||||
args: CardGamePageArgs(
|
||||
|
|
@ -124,8 +131,8 @@ class CardGamePage extends _i8.PageRouteInfo<CardGamePageArgs> {
|
|||
|
||||
static const String name = 'CardGamePage';
|
||||
|
||||
static const _i8.PageInfo<CardGamePageArgs> page =
|
||||
_i8.PageInfo<CardGamePageArgs>(name);
|
||||
static const _i9.PageInfo<CardGamePageArgs> page =
|
||||
_i9.PageInfo<CardGamePageArgs>(name);
|
||||
}
|
||||
|
||||
class CardGamePageArgs {
|
||||
|
|
@ -138,9 +145,9 @@ class CardGamePageArgs {
|
|||
this.key,
|
||||
});
|
||||
|
||||
final List<_i9.GameCardDto> cards;
|
||||
final List<_i10.GameCardDto> cards;
|
||||
|
||||
final _i10.Color? color;
|
||||
final _i11.Color? color;
|
||||
|
||||
final int? startCardId;
|
||||
|
||||
|
|
@ -148,7 +155,7 @@ class CardGamePageArgs {
|
|||
|
||||
final bool shuffle;
|
||||
|
||||
final _i11.Key? key;
|
||||
final _i12.Key? key;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
|
|
@ -158,11 +165,11 @@ class CardGamePageArgs {
|
|||
|
||||
/// generated route for
|
||||
/// [_i3.CardPackPage]
|
||||
class CardPackPage extends _i8.PageRouteInfo<CardPackPageArgs> {
|
||||
class CardPackPage extends _i9.PageRouteInfo<CardPackPageArgs> {
|
||||
CardPackPage({
|
||||
required _i9.CardPackPreviewDto previewDto,
|
||||
_i11.Key? key,
|
||||
List<_i8.PageRouteInfo>? children,
|
||||
required _i10.CardPackPreviewDto previewDto,
|
||||
_i12.Key? key,
|
||||
List<_i9.PageRouteInfo>? children,
|
||||
}) : super(
|
||||
CardPackPage.name,
|
||||
args: CardPackPageArgs(
|
||||
|
|
@ -174,8 +181,8 @@ class CardPackPage extends _i8.PageRouteInfo<CardPackPageArgs> {
|
|||
|
||||
static const String name = 'CardPackPage';
|
||||
|
||||
static const _i8.PageInfo<CardPackPageArgs> page =
|
||||
_i8.PageInfo<CardPackPageArgs>(name);
|
||||
static const _i9.PageInfo<CardPackPageArgs> page =
|
||||
_i9.PageInfo<CardPackPageArgs>(name);
|
||||
}
|
||||
|
||||
class CardPackPageArgs {
|
||||
|
|
@ -184,9 +191,9 @@ class CardPackPageArgs {
|
|||
this.key,
|
||||
});
|
||||
|
||||
final _i9.CardPackPreviewDto previewDto;
|
||||
final _i10.CardPackPreviewDto previewDto;
|
||||
|
||||
final _i11.Key? key;
|
||||
final _i12.Key? key;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
|
|
@ -196,8 +203,8 @@ class CardPackPageArgs {
|
|||
|
||||
/// generated route for
|
||||
/// [_i4.HomePage]
|
||||
class HomePage extends _i8.PageRouteInfo<void> {
|
||||
const HomePage({List<_i8.PageRouteInfo>? children})
|
||||
class HomePage extends _i9.PageRouteInfo<void> {
|
||||
const HomePage({List<_i9.PageRouteInfo>? children})
|
||||
: super(
|
||||
HomePage.name,
|
||||
initialChildren: children,
|
||||
|
|
@ -205,13 +212,13 @@ class HomePage extends _i8.PageRouteInfo<void> {
|
|||
|
||||
static const String name = 'HomePage';
|
||||
|
||||
static const _i8.PageInfo<void> page = _i8.PageInfo<void>(name);
|
||||
static const _i9.PageInfo<void> page = _i9.PageInfo<void>(name);
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [_i5.MainTabsPage]
|
||||
class MainTabsPage extends _i8.PageRouteInfo<void> {
|
||||
const MainTabsPage({List<_i8.PageRouteInfo>? children})
|
||||
class MainTabsPage extends _i9.PageRouteInfo<void> {
|
||||
const MainTabsPage({List<_i9.PageRouteInfo>? children})
|
||||
: super(
|
||||
MainTabsPage.name,
|
||||
initialChildren: children,
|
||||
|
|
@ -219,13 +226,13 @@ class MainTabsPage extends _i8.PageRouteInfo<void> {
|
|||
|
||||
static const String name = 'MainTabsPage';
|
||||
|
||||
static const _i8.PageInfo<void> page = _i8.PageInfo<void>(name);
|
||||
static const _i9.PageInfo<void> page = _i9.PageInfo<void>(name);
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [_i6.ProfilePage]
|
||||
class ProfilePage extends _i8.PageRouteInfo<void> {
|
||||
const ProfilePage({List<_i8.PageRouteInfo>? children})
|
||||
class ProfilePage extends _i9.PageRouteInfo<void> {
|
||||
const ProfilePage({List<_i9.PageRouteInfo>? children})
|
||||
: super(
|
||||
ProfilePage.name,
|
||||
initialChildren: children,
|
||||
|
|
@ -233,15 +240,29 @@ class ProfilePage extends _i8.PageRouteInfo<void> {
|
|||
|
||||
static const String name = 'ProfilePage';
|
||||
|
||||
static const _i8.PageInfo<void> page = _i8.PageInfo<void>(name);
|
||||
static const _i9.PageInfo<void> page = _i9.PageInfo<void>(name);
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [_i7.TestPage]
|
||||
class TestPage extends _i8.PageRouteInfo<TestPageArgs> {
|
||||
/// [_i7.SettingsPage]
|
||||
class SettingsPage extends _i9.PageRouteInfo<void> {
|
||||
const SettingsPage({List<_i9.PageRouteInfo>? children})
|
||||
: super(
|
||||
SettingsPage.name,
|
||||
initialChildren: children,
|
||||
);
|
||||
|
||||
static const String name = 'SettingsPage';
|
||||
|
||||
static const _i9.PageInfo<void> page = _i9.PageInfo<void>(name);
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [_i8.TestPage]
|
||||
class TestPage extends _i9.PageRouteInfo<TestPageArgs> {
|
||||
TestPage({
|
||||
required String testId,
|
||||
List<_i8.PageRouteInfo>? children,
|
||||
List<_i9.PageRouteInfo>? children,
|
||||
}) : super(
|
||||
TestPage.name,
|
||||
args: TestPageArgs(testId: testId),
|
||||
|
|
@ -250,8 +271,8 @@ class TestPage extends _i8.PageRouteInfo<TestPageArgs> {
|
|||
|
||||
static const String name = 'TestPage';
|
||||
|
||||
static const _i8.PageInfo<TestPageArgs> page =
|
||||
_i8.PageInfo<TestPageArgs>(name);
|
||||
static const _i9.PageInfo<TestPageArgs> page =
|
||||
_i9.PageInfo<TestPageArgs>(name);
|
||||
}
|
||||
|
||||
class TestPageArgs {
|
||||
|
|
|
|||
|
|
@ -35,6 +35,15 @@ class Analytics {
|
|||
}.asFirebaseMap);
|
||||
}
|
||||
|
||||
static Future<void> settingsPageOpened(UserDto? dto) {
|
||||
return _analytics.logScreenView(
|
||||
screenClass: 'settings',
|
||||
screenName: 'settings',
|
||||
parameters: {
|
||||
'id': dto?.id,
|
||||
}.asFirebaseMap);
|
||||
}
|
||||
|
||||
static Future<void> availablePackOpened(CardPackDto dto) {
|
||||
return _analytics.logScreenView(
|
||||
screenClass: 'pack',
|
||||
|
|
|
|||
|
|
@ -160,7 +160,7 @@ InputButtonsTestState _$InputButtonsTestStateFromJson(
|
|||
isSkipped: json['isSkipped'] as bool? ?? false,
|
||||
testType:
|
||||
$enumDecodeNullable(_$TestQuestionTypeEnumMap, json['testType']) ??
|
||||
TestQuestionType.simple,
|
||||
TestQuestionType.input_buttons,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$InputButtonsTestStateToJson(
|
||||
|
|
|
|||
|
|
@ -43,9 +43,9 @@ class TestCompleteWidget extends StatelessWidget {
|
|||
.compareTo(n.correctCount.toDouble() / n.totalCount),
|
||||
);
|
||||
return PopScope(
|
||||
onPopInvoked: (v) {
|
||||
onPopInvoked: (v) async {
|
||||
if (v) {
|
||||
locator.testManager.sendStatistics();
|
||||
await locator.testManager.sendStatistics();
|
||||
locator.testManager.triggerPoll();
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ class _TestPageState extends State<TestPage> {
|
|||
print('AUDIO $_lastAudioQuestionId ${question.id}');
|
||||
_lastAudioQuestionId = question.id;
|
||||
try {
|
||||
if (globalSharedPreferences.getBool('auto_play_sound') != false) {
|
||||
if (globalSharedPreferences.getBool('auto_play_sound_tests') != false) {
|
||||
AudioPlayer.playAudio((question as dynamic).audio);
|
||||
}
|
||||
} catch (e, s) {}
|
||||
|
|
|
|||
|
|
@ -100,9 +100,13 @@ Future<void> _fillSp() async {
|
|||
if (soundSpeed == null) {
|
||||
globalSharedPreferences.setDouble('sound_speed', 1.0);
|
||||
}
|
||||
final autoPlay = globalSharedPreferences.getBool('auto_play_sound');
|
||||
final autoPlay = globalSharedPreferences.getBool('auto_play_sound_tests');
|
||||
if (autoPlay == null) {
|
||||
globalSharedPreferences.setBool('auto_play_sound', true);
|
||||
globalSharedPreferences.setBool('auto_play_sound_tests', true);
|
||||
}
|
||||
final autoPlayView = globalSharedPreferences.getBool('auto_play_sound_view');
|
||||
if (autoPlayView == null) {
|
||||
globalSharedPreferences.setBool('auto_play_sound_view', false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,20 @@ class HttpRepository extends Repository with Api {
|
|||
@override
|
||||
Future<void> updateUser(UserDto dto) async {}
|
||||
|
||||
@override
|
||||
Future<PromoCodeDto> applyPromocode(String code) async {
|
||||
try {
|
||||
final r = await _dio.post<String>(
|
||||
'$path/user/promocode',
|
||||
data: PromoCodeDto(code: code).encode(),
|
||||
);
|
||||
return (r.data as String).decode(PromoCodeDto.fromJson);
|
||||
} on DioException catch (e, s) {
|
||||
log('User create error', error: e, stackTrace: s);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<(UserDto, String)> createOrGetUser(
|
||||
String externalId,
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ abstract class Repository {
|
|||
|
||||
Future<void> updateUser(UserDto dto);
|
||||
|
||||
Future<void> applyPromocode(String promocode);
|
||||
|
||||
Future<(UserDto, String)> createOrGetUser(
|
||||
String externalId,
|
||||
ExternalIdType idType,
|
||||
|
|
@ -24,7 +26,7 @@ abstract class Repository {
|
|||
|
||||
Future<void> addTestStatistics(TestStatisticsDto testStatistics);
|
||||
|
||||
Future<List<TestDto>> getTests();
|
||||
Future<List<TestDto>> getTests();
|
||||
|
||||
Future<TestDto?> getTest(String id);
|
||||
|
||||
|
|
|
|||
|
|
@ -74,6 +74,11 @@ class UserManager {
|
|||
await _sharedPreferences!.remove('authToken');
|
||||
}
|
||||
|
||||
Future<PromoCodeDto> applyPromocode(String code) async {
|
||||
final data = await _repository.applyPromocode(code);
|
||||
return data;
|
||||
}
|
||||
|
||||
Future<UserDto?> updateUser() async {
|
||||
String? authToken = _sharedPreferences!.getString('authToken');
|
||||
if (authToken != null) {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,9 @@ import 'package:flutter/cupertino.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:mnemo_cards/domain/router/app_router.dart';
|
||||
import 'package:mnemo_cards/domain/router/app_router.gr.dart';
|
||||
import 'package:mnemo_cards/flags.dart';
|
||||
import 'package:mnemo_cards/main.dart';
|
||||
import 'package:mnemo_cards/widgets/header.dart';
|
||||
import 'package:mnemo_cards_common/mnemo_cards_common.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
|
@ -17,6 +19,7 @@ import '../di/locator.dart';
|
|||
import '../features/analytics/analytics.dart';
|
||||
import '../theme/themes.dart';
|
||||
import '../widgets/big_back_button.dart';
|
||||
import '../widgets/enter_promo_code_widget.dart';
|
||||
import '../widgets/shared_pref_button.dart';
|
||||
import '../widgets/simple_tile.dart';
|
||||
import '../widgets/slider.dart';
|
||||
|
|
@ -59,123 +62,112 @@ class ProfilePage extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 8.h,),
|
||||
SizedBox(
|
||||
height: 8.h,
|
||||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
StreamBuilder(
|
||||
stream: locator.userManager.userStateHolder.asStream,
|
||||
builder: (context, snapshot) {
|
||||
final userData = snapshot.data?.userDataDto;
|
||||
return Column(
|
||||
children: [
|
||||
if (ADMIN_BUILD)
|
||||
MaterialButton(
|
||||
onPressed: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (c) => const AllCards());
|
||||
},
|
||||
child: Text('ALL CARDS'),
|
||||
),
|
||||
if (ADMIN_BUILD)
|
||||
MaterialButton(
|
||||
onPressed: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (c) => const AllUsers());
|
||||
},
|
||||
child: Text('ALL USERS'),
|
||||
),
|
||||
Padding(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 10.0),
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8.0),
|
||||
child: UserStatistics(userData),
|
||||
),
|
||||
SharedPrefButton<bool>.builder(
|
||||
spKey: 'sound_on',
|
||||
builder: (enabled, _) => AbsorbPointer(
|
||||
child: SwitchTile(
|
||||
text: 'Звук',
|
||||
value: enabled ?? false,
|
||||
),
|
||||
),
|
||||
setOnTap: (v) => !(v ?? false),
|
||||
),
|
||||
Divider(
|
||||
height: 1,
|
||||
color: borderGray.withOpacity(0.2),
|
||||
),
|
||||
SharedPrefButton<bool>.builder(
|
||||
spKey: 'auto_play_sound',
|
||||
builder: (enabled, _) => AbsorbPointer(
|
||||
child: SwitchTile(
|
||||
text: 'Авто воспроизведение в тестах',
|
||||
value: enabled ?? false,
|
||||
),
|
||||
),
|
||||
setOnTap: (v) => !(v ?? false),
|
||||
),
|
||||
Divider(
|
||||
height: 5,
|
||||
color: borderGray.withOpacity(0.2),
|
||||
),
|
||||
SimpleTile(
|
||||
text: 'Скорость чтения',
|
||||
trail: SharedPrefButton<double>.builder(
|
||||
spKey: 'sound_speed',
|
||||
builder: (v, s) => MnemoSlider(
|
||||
onChanged: (value) => s(value),
|
||||
value: v ?? 1.0,
|
||||
),
|
||||
shouldRebuild: (v) => false,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
},
|
||||
if (ADMIN_BUILD)
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: MaterialButton(
|
||||
onPressed: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (c) => const AllCards());
|
||||
},
|
||||
child: Text('ALL CARDS'),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: MaterialButton(
|
||||
onPressed: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (c) => const AllUsers());
|
||||
},
|
||||
child: Text('ALL USERS'),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Expanded(
|
||||
child: StreamBuilder(
|
||||
stream: locator.userManager.userStateHolder.asStream,
|
||||
builder: (context, snapshot) {
|
||||
final userData = snapshot.data?.userDataDto;
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 10.0,
|
||||
right: 10.0,
|
||||
bottom: 8.0,
|
||||
),
|
||||
child: UserStatistics(userData),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 10.0.w),
|
||||
child: LinkButton(
|
||||
'Написать в тг',
|
||||
() => launchUrl(
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10.0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Divider(
|
||||
height: 5,
|
||||
color: borderGray.withOpacity(0.2),
|
||||
),
|
||||
const EnterPromoCodeWidget(),
|
||||
Divider(
|
||||
height: 5,
|
||||
color: borderGray.withOpacity(0.2),
|
||||
),
|
||||
SimpleTile.text(
|
||||
text: 'Написать в телеграм',
|
||||
onTap: () => launchUrl(
|
||||
Uri.parse('https://t.me/mnemo_cards_bot'),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: 10.0.h,
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 10.0.w),
|
||||
child: LinkButton(
|
||||
'Политика конфиденциальности',
|
||||
() {
|
||||
launchUrl(
|
||||
Uri.parse(
|
||||
'https://www.freeprivacypolicy.com/live/1c2bce51-86c6-4a36-b226-b6c6f50ebf0f'),
|
||||
);
|
||||
},
|
||||
Divider(
|
||||
height: 5,
|
||||
color: borderGray.withOpacity(0.2),
|
||||
),
|
||||
),
|
||||
],
|
||||
SimpleTile(
|
||||
onTap: () => appRouter.push(
|
||||
PageRouteInfo(SettingsPage.name),
|
||||
),
|
||||
title: Row(
|
||||
children: [
|
||||
Image.asset(
|
||||
'icons/settings.png',
|
||||
width: 16.w,
|
||||
),
|
||||
SizedBox(width: 8.w),
|
||||
Expanded(child: Text('Настройки там')),
|
||||
],
|
||||
),
|
||||
trail: Image.asset(
|
||||
'icons/next.png',
|
||||
width: 16.w,
|
||||
),
|
||||
),
|
||||
Divider(
|
||||
height: 5,
|
||||
color: borderGray.withOpacity(0.2),
|
||||
),
|
||||
SizedBox(
|
||||
height: 10.0.h,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const BigBackButton(),
|
||||
if (MediaQuery.of(context).viewInsets.bottom < 30.0)
|
||||
const BigBackButton(),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
|
|||
157
lib/pages/settgins_page.dart
Normal file
157
lib/pages/settgins_page.dart
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:mnemo_cards/domain/router/app_router.dart';
|
||||
import 'package:mnemo_cards/flags.dart';
|
||||
import 'package:mnemo_cards/widgets/header.dart';
|
||||
import 'package:mnemo_cards_common/mnemo_cards_common.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
import '../admin/all_cards.dart';
|
||||
import '../admin/all_users.dart';
|
||||
import '../di/locator.dart';
|
||||
import '../features/analytics/analytics.dart';
|
||||
import '../theme/themes.dart';
|
||||
import '../widgets/big_back_button.dart';
|
||||
import '../widgets/enter_promo_code_widget.dart';
|
||||
import '../widgets/shared_pref_button.dart';
|
||||
import '../widgets/simple_tile.dart';
|
||||
import '../widgets/slider.dart';
|
||||
import '../widgets/switch_button.dart';
|
||||
import '../widgets/switch_tile.dart';
|
||||
import '../widgets/text_button.dart';
|
||||
import '../widgets/user_statistics.dart';
|
||||
|
||||
@RoutePage()
|
||||
class SettingsPage extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Analytics.settingsPageOpened(locator.userManager.userStateHolder.user);
|
||||
return Scaffold(
|
||||
body: SafeArea(
|
||||
child: Column(
|
||||
children: [
|
||||
Header(
|
||||
'Настройки',
|
||||
popText: 'назад',
|
||||
hasPopButton: true,
|
||||
),
|
||||
SizedBox(
|
||||
height: 8.h,
|
||||
),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 10.0.w),
|
||||
child: Expanded(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
Divider(
|
||||
height: 1,
|
||||
color: borderGray.withOpacity(0.2),
|
||||
),
|
||||
SharedPrefButton<bool>.builder(
|
||||
spKey: 'sound_on',
|
||||
builder: (enabled, _) => AbsorbPointer(
|
||||
child: SwitchTile(
|
||||
text: 'Звук',
|
||||
value: enabled ?? false,
|
||||
),
|
||||
),
|
||||
setOnTap: (v) => !(v ?? false),
|
||||
),
|
||||
Divider(
|
||||
height: 1,
|
||||
color: borderGray.withOpacity(0.2),
|
||||
),
|
||||
SharedPrefButton<bool>.builder(
|
||||
spKey: 'auto_play_sound_view',
|
||||
builder: (enabled, _) => AbsorbPointer(
|
||||
child: SwitchTile(
|
||||
text: 'Авто воспроизведение при просмотре',
|
||||
value: enabled ?? false,
|
||||
),
|
||||
),
|
||||
setOnTap: (v) => !(v ?? false),
|
||||
),
|
||||
Divider(
|
||||
height: 1,
|
||||
color: borderGray.withOpacity(0.2),
|
||||
),
|
||||
SharedPrefButton<bool>.builder(
|
||||
spKey: 'auto_play_sound_tests',
|
||||
builder: (enabled, _) => AbsorbPointer(
|
||||
child: SwitchTile(
|
||||
text: 'Авто воспроизведение в тестах',
|
||||
value: enabled ?? false,
|
||||
),
|
||||
),
|
||||
setOnTap: (v) => !(v ?? false),
|
||||
),
|
||||
Divider(
|
||||
height: 5,
|
||||
color: borderGray.withOpacity(0.2),
|
||||
),
|
||||
SimpleTile.text(
|
||||
text: 'Скорость чтения',
|
||||
trail: SharedPrefButton<double>.builder(
|
||||
spKey: 'sound_speed',
|
||||
builder: (v, s) => MnemoSlider(
|
||||
onChanged: (value) => s(value),
|
||||
value: v ?? 1.0,
|
||||
),
|
||||
shouldRebuild: (v) => false,
|
||||
),
|
||||
),
|
||||
Divider(
|
||||
height: 1,
|
||||
color: borderGray.withOpacity(0.2),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Column(
|
||||
children: [
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 32.0.h,
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 10.0.w),
|
||||
child: LinkButton(
|
||||
'Политика конфиденциальности',
|
||||
() {
|
||||
launchUrl(
|
||||
Uri.parse(
|
||||
'https://www.freeprivacypolicy.com/live/1c2bce51-86c6-4a36-b226-b6c6f50ebf0f'),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const BigBackButton(),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> clearAndExit() async {
|
||||
final sp = await SharedPreferences.getInstance();
|
||||
sp.clear();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
const SettingsPage();
|
||||
}
|
||||
|
|
@ -1,9 +1,12 @@
|
|||
import 'dart:async';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:mnemo_cards/features/tests/progress_widget.dart';
|
||||
import 'package:mnemo_cards/main.dart';
|
||||
import 'package:mnemo_cards/managers/audio_player.dart';
|
||||
import 'package:mnemo_cards/theme/themes.dart';
|
||||
import 'package:mnemo_cards_common/mnemo_cards_common.dart';
|
||||
import 'package:rxdart/rxdart.dart';
|
||||
|
|
@ -37,6 +40,8 @@ class _CardGamePageState extends State<CardGamePage> {
|
|||
final CardSwiperController cardSwiperController = CardSwiperController();
|
||||
final FlipCardController flipCardController = FlipCardController();
|
||||
|
||||
StreamSubscription? _sub;
|
||||
|
||||
List<GameCardDto> shuffledCards = [];
|
||||
|
||||
GameCardDto? get activeCard {
|
||||
|
|
@ -49,6 +54,15 @@ class _CardGamePageState extends State<CardGamePage> {
|
|||
|
||||
int get gameLength => widget.infinite ? 10000000 : shuffledCards.length;
|
||||
|
||||
void playSound(int index) {
|
||||
if (globalSharedPreferences.getBool('auto_play_sound_view') == true) {
|
||||
final text = activeCard?.original;
|
||||
if (text != null) {
|
||||
AudioPlayer.playAudio(text, lang: 'es-ES');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// todo move to controller
|
||||
|
|
@ -212,11 +226,8 @@ class _CardGamePageState extends State<CardGamePage> {
|
|||
flex: 3,
|
||||
child: InkWell(
|
||||
onTap: () async {
|
||||
final direction = Random().nextBool()
|
||||
? CardSwiperState.swipeLeft
|
||||
: CardSwiperState.swipeRight;
|
||||
cardSwiperController.swipe(
|
||||
direction: direction,
|
||||
direction: CardSwiperState.swipeLeft,
|
||||
);
|
||||
},
|
||||
child: Container(
|
||||
|
|
@ -276,10 +287,12 @@ class _CardGamePageState extends State<CardGamePage> {
|
|||
} else {
|
||||
shuffledCards = widget.cards.toList();
|
||||
}
|
||||
_sub = cardSwiperController.asStream.distinct().listen(playSound);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_sub?.cancel().then((_) => _sub = null);
|
||||
// cardSwiperController.removeListener(cardSwiperListener);
|
||||
cardSwiperController.dispose();
|
||||
super.dispose();
|
||||
|
|
|
|||
88
lib/widgets/enter_promo_code_widget.dart
Normal file
88
lib/widgets/enter_promo_code_widget.dart
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:mnemo_cards/di/locator.dart';
|
||||
import 'package:mnemo_cards/theme/themes.dart';
|
||||
import 'package:mnemo_cards/widgets/simple_tile.dart';
|
||||
|
||||
class EnterPromoCodeWidget extends StatefulWidget {
|
||||
const EnterPromoCodeWidget();
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _EnterPromoCodeState();
|
||||
}
|
||||
|
||||
class _EnterPromoCodeState extends State<EnterPromoCodeWidget> {
|
||||
final TextEditingController controller = TextEditingController();
|
||||
|
||||
String? message;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
SimpleTile(
|
||||
title: Row(
|
||||
children: [
|
||||
Text('Промокод'),
|
||||
SizedBox(
|
||||
width: 16.w,
|
||||
),
|
||||
Expanded(
|
||||
child: TextField(
|
||||
// cursorHeight: 17.h,
|
||||
controller: controller,
|
||||
textAlignVertical: TextAlignVertical.center,
|
||||
style: TextStyle(fontSize: 17.sp),
|
||||
decoration: InputDecoration(
|
||||
contentPadding: EdgeInsets.all(8.0),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: 8.w,
|
||||
),
|
||||
],
|
||||
),
|
||||
trail: GestureDetector(
|
||||
onTap: () async {
|
||||
final result = await locator.userManager.applyPromocode(
|
||||
controller.text,
|
||||
);
|
||||
if (result.success == true) {
|
||||
controller.clear();
|
||||
}
|
||||
setState(() {
|
||||
message = result.message;
|
||||
});
|
||||
FocusScope.of(context).unfocus();
|
||||
},
|
||||
child: AspectRatio(
|
||||
aspectRatio: 1.0,
|
||||
child: Container(
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
color: progressBlue,
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
),
|
||||
child: Image.asset(
|
||||
'icons/gift.png',
|
||||
width: 17.w,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (message != null && message!.isNotEmpty)
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4.0),
|
||||
child: Text(message!),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,24 +1,41 @@
|
|||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:mnemo_cards/theme/themes.dart';
|
||||
import 'package:mnemo_cards/widgets/switch_button.dart';
|
||||
|
||||
class SimpleTile extends StatelessWidget {
|
||||
final Widget? trail;
|
||||
final String text;
|
||||
final Widget title;
|
||||
final VoidCallback? onTap;
|
||||
|
||||
SimpleTile({
|
||||
required this.text,
|
||||
required this.title,
|
||||
this.trail,
|
||||
this.onTap,
|
||||
});
|
||||
|
||||
SimpleTile.text({
|
||||
required String text,
|
||||
this.trail,
|
||||
this.onTap,
|
||||
}) : title = Text(text);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
children: [
|
||||
Expanded(child: Text(text)),
|
||||
if (trail != null) trail!,
|
||||
],
|
||||
return GestureDetector(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
height: 50.h,
|
||||
padding: EdgeInsets.symmetric(vertical: 4.h),
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(child: title),
|
||||
if (trail != null) trail!,
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,16 +24,17 @@ class MnemoSlider extends StatelessWidget {
|
|||
var v = this.value;
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: borderGray),
|
||||
borderRadius: BorderRadius.circular(12.0)),
|
||||
padding: EdgeInsets.symmetric(vertical: 1.0, horizontal: 2.0),
|
||||
border: Border.all(color: borderGray),
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
),
|
||||
padding: EdgeInsets.symmetric(vertical: 3, horizontal: 2.0),
|
||||
child: SliderTheme(
|
||||
data: SliderTheme.of(context).copyWith(
|
||||
activeTrackColor: Colors.white,
|
||||
inactiveTrackColor: Colors.white,
|
||||
thumbColor: progressBlue,
|
||||
thumbShape: _RectSliderThumbShape(
|
||||
enabledThumbRadius: 18.0,
|
||||
enabledThumbRadius: 19.0,
|
||||
borderRadius: Radius.circular(10),
|
||||
textBuilder: (v) {
|
||||
final value = ((v * (max - min) + min) * 10).toInt();
|
||||
|
|
@ -45,7 +46,7 @@ class MnemoSlider extends StatelessWidget {
|
|||
},
|
||||
),
|
||||
trackShape: RoundedRectSliderTrackShape(),
|
||||
trackHeight: 36.h,
|
||||
trackHeight: 1.h,
|
||||
overlayColor: Colors.transparent,
|
||||
overlappingShapeStrokeColor: Colors.transparent,
|
||||
overlayShape: RoundSliderOverlayShape(overlayRadius: 2.0),
|
||||
|
|
@ -119,19 +120,20 @@ class _RectSliderThumbShape extends SliderComponentShape {
|
|||
}
|
||||
|
||||
@override
|
||||
void paint(PaintingContext context,
|
||||
Offset center, {
|
||||
required Animation<double> activationAnimation,
|
||||
required Animation<double> enableAnimation,
|
||||
required bool isDiscrete,
|
||||
required TextPainter labelPainter,
|
||||
required RenderBox parentBox,
|
||||
required SliderThemeData sliderTheme,
|
||||
required TextDirection textDirection,
|
||||
required double value,
|
||||
required double textScaleFactor,
|
||||
required Size sizeWithOverflow,
|
||||
}) {
|
||||
void paint(
|
||||
PaintingContext context,
|
||||
Offset center, {
|
||||
required Animation<double> activationAnimation,
|
||||
required Animation<double> enableAnimation,
|
||||
required bool isDiscrete,
|
||||
required TextPainter labelPainter,
|
||||
required RenderBox parentBox,
|
||||
required SliderThemeData sliderTheme,
|
||||
required TextDirection textDirection,
|
||||
required double value,
|
||||
required double textScaleFactor,
|
||||
required Size sizeWithOverflow,
|
||||
}) {
|
||||
final Canvas canvas = context.canvas;
|
||||
final Tween<double> radiusTween = Tween<double>(
|
||||
begin: _disabledThumbRadius,
|
||||
|
|
@ -151,7 +153,7 @@ class _RectSliderThumbShape extends SliderComponentShape {
|
|||
);
|
||||
|
||||
final double evaluatedElevation =
|
||||
elevationTween.evaluate(activationAnimation);
|
||||
elevationTween.evaluate(activationAnimation);
|
||||
final Path path = Path()
|
||||
..addArc(
|
||||
Rect.fromCenter(
|
||||
|
|
@ -172,7 +174,7 @@ class _RectSliderThumbShape extends SliderComponentShape {
|
|||
}
|
||||
|
||||
final TextPainter textPainter =
|
||||
TextPainter(textDirection: TextDirection.rtl);
|
||||
TextPainter(textDirection: TextDirection.rtl);
|
||||
textPainter.text = TextSpan(
|
||||
text: textBuilder?.call(value) ?? '',
|
||||
style: TextStyle(
|
||||
|
|
@ -190,8 +192,7 @@ class _RectSliderThumbShape extends SliderComponentShape {
|
|||
Rect.fromCircle(center: center, radius: radius),
|
||||
borderRadius ?? Radius.zero,
|
||||
),
|
||||
Paint()
|
||||
..color = borderGray,
|
||||
Paint()..color = borderGray,
|
||||
);
|
||||
// Use drawRect instead of drawCircle
|
||||
canvas.drawRRect(
|
||||
|
|
@ -199,8 +200,7 @@ class _RectSliderThumbShape extends SliderComponentShape {
|
|||
Rect.fromCircle(center: center, radius: radius - 1),
|
||||
borderRadius ?? Radius.zero,
|
||||
),
|
||||
Paint()
|
||||
..color = color,
|
||||
Paint()..color = color,
|
||||
);
|
||||
|
||||
textPainter.paint(canvas, textCenter);
|
||||
|
|
|
|||
|
|
@ -51,51 +51,54 @@ class UserStatistics extends StatelessWidget {
|
|||
],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
height: 200.h,
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: borderGray),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
padding: EdgeInsets.symmetric(horizontal: 8.0.w),
|
||||
child: words.isEmpty
|
||||
? Center(
|
||||
child: AutoSizeText('Тут будут все пройденные слова'),
|
||||
)
|
||||
: ListView.builder(
|
||||
itemCount: words.length,
|
||||
shrinkWrap: true,
|
||||
itemBuilder: (context, index) {
|
||||
final word = words[index];
|
||||
return Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 2.0.h),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'${word.word}',
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
child: SizedBox(
|
||||
width: MediaQuery.of(context).size.width / 2.5,
|
||||
child: HorizontalProgressWidget(
|
||||
(word.correct * 100).floor(),
|
||||
(word.total * 100).floor(),
|
||||
learnedWords.contains(word.word) ? golden : progressBlue,
|
||||
borderColor: borderGray,
|
||||
height: 20.h,
|
||||
Expanded(
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: borderGray),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
padding: EdgeInsets.symmetric(horizontal: 8.0.w),
|
||||
child: words.isEmpty
|
||||
? Center(
|
||||
child: AutoSizeText('Тут будут все пройденные слова'),
|
||||
)
|
||||
: ListView.builder(
|
||||
itemCount: words.length,
|
||||
shrinkWrap: true,
|
||||
itemBuilder: (context, index) {
|
||||
final word = words[index];
|
||||
return Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 2.0.h),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'${word.word}',
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
Flexible(
|
||||
child: SizedBox(
|
||||
width: MediaQuery.of(context).size.width / 2.5,
|
||||
child: HorizontalProgressWidget(
|
||||
(word.correct * 100).floor(),
|
||||
(word.total * 100).floor(),
|
||||
learnedWords.contains(word.word)
|
||||
? golden
|
||||
: progressBlue,
|
||||
borderColor: borderGray,
|
||||
height: 20.h,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
|
|
|||
Loading…
Reference in a new issue