end widget
This commit is contained in:
parent
d2c04fc2c8
commit
b3344ae794
21 changed files with 403 additions and 168 deletions
BIN
icons/crown.png
Normal file
BIN
icons/crown.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 351 B |
BIN
icons/skull.png
Normal file
BIN
icons/skull.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 563 B |
BIN
icons/small_circle.png
Normal file
BIN
icons/small_circle.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 312 B |
|
|
@ -22,6 +22,16 @@ class ProgressWidget extends StatefulWidget {
|
||||||
@override
|
@override
|
||||||
State<StatefulWidget> createState() => _ProgressWidgetState();
|
State<StatefulWidget> createState() => _ProgressWidgetState();
|
||||||
|
|
||||||
|
factory ProgressWidget.value(int value, int length, Color color) =>
|
||||||
|
ProgressWidget(
|
||||||
|
List.generate(
|
||||||
|
length,
|
||||||
|
(i) => i < value ? Result.correct : Result.not_visited,
|
||||||
|
),
|
||||||
|
length,
|
||||||
|
color,
|
||||||
|
);
|
||||||
|
|
||||||
ProgressWidget(
|
ProgressWidget(
|
||||||
this.results,
|
this.results,
|
||||||
this.length,
|
this.length,
|
||||||
|
|
@ -156,36 +166,6 @@ class _ProgressWidgetState extends State<ProgressWidget> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget correctAnswers() {
|
|
||||||
return Align(
|
|
||||||
alignment: Alignment.bottomLeft,
|
|
||||||
child: Container(
|
|
||||||
height: 6,
|
|
||||||
alignment: Alignment.bottomLeft,
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
...List.generate(
|
|
||||||
length,
|
|
||||||
(index) => Expanded(
|
|
||||||
child: AnimatedContainer(
|
|
||||||
curve: Curves.linear,
|
|
||||||
duration: Duration(milliseconds: 300),
|
|
||||||
color: index < results.length
|
|
||||||
? results[index] == Result.correct
|
|
||||||
? Colors.lightGreenAccent[200]
|
|
||||||
: results[index] == Result.wrong
|
|
||||||
? Colors.redAccent[100]
|
|
||||||
: Colors.white
|
|
||||||
: Colors.transparent,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
_ProgressWidgetState();
|
_ProgressWidgetState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ class InputButtonsTestState extends TestQuestionState {
|
||||||
|
|
||||||
InputButtonsTestState({
|
InputButtonsTestState({
|
||||||
required this.seed,
|
required this.seed,
|
||||||
|
required super.word,
|
||||||
this.answer = const [],
|
this.answer = const [],
|
||||||
this.answerIds = const [],
|
this.answerIds = const [],
|
||||||
this.isCorrect = false,
|
this.isCorrect = false,
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,8 @@ part of 'input_buttons_test_state.dart';
|
||||||
abstract class _$InputButtonsTestStateCWProxy {
|
abstract class _$InputButtonsTestStateCWProxy {
|
||||||
InputButtonsTestState seed(int seed);
|
InputButtonsTestState seed(int seed);
|
||||||
|
|
||||||
|
InputButtonsTestState word(String word);
|
||||||
|
|
||||||
InputButtonsTestState answer(List<String> answer);
|
InputButtonsTestState answer(List<String> answer);
|
||||||
|
|
||||||
InputButtonsTestState answerIds(List<String> answerIds);
|
InputButtonsTestState answerIds(List<String> answerIds);
|
||||||
|
|
@ -29,6 +31,7 @@ abstract class _$InputButtonsTestStateCWProxy {
|
||||||
/// ````
|
/// ````
|
||||||
InputButtonsTestState call({
|
InputButtonsTestState call({
|
||||||
int? seed,
|
int? seed,
|
||||||
|
String? word,
|
||||||
List<String>? answer,
|
List<String>? answer,
|
||||||
List<String>? answerIds,
|
List<String>? answerIds,
|
||||||
bool? isCorrect,
|
bool? isCorrect,
|
||||||
|
|
@ -48,6 +51,9 @@ class _$InputButtonsTestStateCWProxyImpl
|
||||||
@override
|
@override
|
||||||
InputButtonsTestState seed(int seed) => this(seed: seed);
|
InputButtonsTestState seed(int seed) => this(seed: seed);
|
||||||
|
|
||||||
|
@override
|
||||||
|
InputButtonsTestState word(String word) => this(word: word);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
InputButtonsTestState answer(List<String> answer) => this(answer: answer);
|
InputButtonsTestState answer(List<String> answer) => this(answer: answer);
|
||||||
|
|
||||||
|
|
@ -79,6 +85,7 @@ class _$InputButtonsTestStateCWProxyImpl
|
||||||
/// ````
|
/// ````
|
||||||
InputButtonsTestState call({
|
InputButtonsTestState call({
|
||||||
Object? seed = const $CopyWithPlaceholder(),
|
Object? seed = const $CopyWithPlaceholder(),
|
||||||
|
Object? word = const $CopyWithPlaceholder(),
|
||||||
Object? answer = const $CopyWithPlaceholder(),
|
Object? answer = const $CopyWithPlaceholder(),
|
||||||
Object? answerIds = const $CopyWithPlaceholder(),
|
Object? answerIds = const $CopyWithPlaceholder(),
|
||||||
Object? isCorrect = const $CopyWithPlaceholder(),
|
Object? isCorrect = const $CopyWithPlaceholder(),
|
||||||
|
|
@ -91,6 +98,10 @@ class _$InputButtonsTestStateCWProxyImpl
|
||||||
? _value.seed
|
? _value.seed
|
||||||
// ignore: cast_nullable_to_non_nullable
|
// ignore: cast_nullable_to_non_nullable
|
||||||
: seed as int,
|
: seed as int,
|
||||||
|
word: word == const $CopyWithPlaceholder() || word == null
|
||||||
|
? _value.word
|
||||||
|
// ignore: cast_nullable_to_non_nullable
|
||||||
|
: word as String,
|
||||||
answer: answer == const $CopyWithPlaceholder() || answer == null
|
answer: answer == const $CopyWithPlaceholder() || answer == null
|
||||||
? _value.answer
|
? _value.answer
|
||||||
// ignore: cast_nullable_to_non_nullable
|
// ignore: cast_nullable_to_non_nullable
|
||||||
|
|
@ -135,6 +146,7 @@ InputButtonsTestState _$InputButtonsTestStateFromJson(
|
||||||
Map<String, dynamic> json) =>
|
Map<String, dynamic> json) =>
|
||||||
InputButtonsTestState(
|
InputButtonsTestState(
|
||||||
seed: (json['seed'] as num).toInt(),
|
seed: (json['seed'] as num).toInt(),
|
||||||
|
word: json['word'] as String,
|
||||||
answer: (json['answer'] as List<dynamic>?)
|
answer: (json['answer'] as List<dynamic>?)
|
||||||
?.map((e) => e as String)
|
?.map((e) => e as String)
|
||||||
.toList() ??
|
.toList() ??
|
||||||
|
|
@ -155,6 +167,7 @@ Map<String, dynamic> _$InputButtonsTestStateToJson(
|
||||||
InputButtonsTestState instance) =>
|
InputButtonsTestState instance) =>
|
||||||
<String, dynamic>{
|
<String, dynamic>{
|
||||||
'testType': _$TestQuestionTypeEnumMap[instance.testType]!,
|
'testType': _$TestQuestionTypeEnumMap[instance.testType]!,
|
||||||
|
'word': instance.word,
|
||||||
'answer': instance.answer,
|
'answer': instance.answer,
|
||||||
'answerIds': instance.answerIds,
|
'answerIds': instance.answerIds,
|
||||||
'isCorrect': instance.isCorrect,
|
'isCorrect': instance.isCorrect,
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ class SimpleTestState extends TestQuestionState {
|
||||||
|
|
||||||
SimpleTestState({
|
SimpleTestState({
|
||||||
required this.seed,
|
required this.seed,
|
||||||
|
required super.word,
|
||||||
this.answer = '',
|
this.answer = '',
|
||||||
this.isCorrect = false,
|
this.isCorrect = false,
|
||||||
this.isAnswered = false,
|
this.isAnswered = false,
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,8 @@ part of 'simple_test_state.dart';
|
||||||
abstract class _$SimpleTestStateCWProxy {
|
abstract class _$SimpleTestStateCWProxy {
|
||||||
SimpleTestState seed(int seed);
|
SimpleTestState seed(int seed);
|
||||||
|
|
||||||
|
SimpleTestState word(String word);
|
||||||
|
|
||||||
SimpleTestState answer(String answer);
|
SimpleTestState answer(String answer);
|
||||||
|
|
||||||
SimpleTestState isCorrect(bool isCorrect);
|
SimpleTestState isCorrect(bool isCorrect);
|
||||||
|
|
@ -27,6 +29,7 @@ abstract class _$SimpleTestStateCWProxy {
|
||||||
/// ````
|
/// ````
|
||||||
SimpleTestState call({
|
SimpleTestState call({
|
||||||
int? seed,
|
int? seed,
|
||||||
|
String? word,
|
||||||
String? answer,
|
String? answer,
|
||||||
bool? isCorrect,
|
bool? isCorrect,
|
||||||
bool? isAnswered,
|
bool? isAnswered,
|
||||||
|
|
@ -44,6 +47,9 @@ class _$SimpleTestStateCWProxyImpl implements _$SimpleTestStateCWProxy {
|
||||||
@override
|
@override
|
||||||
SimpleTestState seed(int seed) => this(seed: seed);
|
SimpleTestState seed(int seed) => this(seed: seed);
|
||||||
|
|
||||||
|
@override
|
||||||
|
SimpleTestState word(String word) => this(word: word);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
SimpleTestState answer(String answer) => this(answer: answer);
|
SimpleTestState answer(String answer) => this(answer: answer);
|
||||||
|
|
||||||
|
|
@ -70,6 +76,7 @@ class _$SimpleTestStateCWProxyImpl implements _$SimpleTestStateCWProxy {
|
||||||
/// ````
|
/// ````
|
||||||
SimpleTestState call({
|
SimpleTestState call({
|
||||||
Object? seed = const $CopyWithPlaceholder(),
|
Object? seed = const $CopyWithPlaceholder(),
|
||||||
|
Object? word = const $CopyWithPlaceholder(),
|
||||||
Object? answer = const $CopyWithPlaceholder(),
|
Object? answer = const $CopyWithPlaceholder(),
|
||||||
Object? isCorrect = const $CopyWithPlaceholder(),
|
Object? isCorrect = const $CopyWithPlaceholder(),
|
||||||
Object? isAnswered = const $CopyWithPlaceholder(),
|
Object? isAnswered = const $CopyWithPlaceholder(),
|
||||||
|
|
@ -81,6 +88,10 @@ class _$SimpleTestStateCWProxyImpl implements _$SimpleTestStateCWProxy {
|
||||||
? _value.seed
|
? _value.seed
|
||||||
// ignore: cast_nullable_to_non_nullable
|
// ignore: cast_nullable_to_non_nullable
|
||||||
: seed as int,
|
: seed as int,
|
||||||
|
word: word == const $CopyWithPlaceholder() || word == null
|
||||||
|
? _value.word
|
||||||
|
// ignore: cast_nullable_to_non_nullable
|
||||||
|
: word as String,
|
||||||
answer: answer == const $CopyWithPlaceholder() || answer == null
|
answer: answer == const $CopyWithPlaceholder() || answer == null
|
||||||
? _value.answer
|
? _value.answer
|
||||||
// ignore: cast_nullable_to_non_nullable
|
// ignore: cast_nullable_to_non_nullable
|
||||||
|
|
@ -119,6 +130,7 @@ extension $SimpleTestStateCopyWith on SimpleTestState {
|
||||||
SimpleTestState _$SimpleTestStateFromJson(Map<String, dynamic> json) =>
|
SimpleTestState _$SimpleTestStateFromJson(Map<String, dynamic> json) =>
|
||||||
SimpleTestState(
|
SimpleTestState(
|
||||||
seed: (json['seed'] as num).toInt(),
|
seed: (json['seed'] as num).toInt(),
|
||||||
|
word: json['word'] as String,
|
||||||
answer: json['answer'] as String? ?? '',
|
answer: json['answer'] as String? ?? '',
|
||||||
isCorrect: json['isCorrect'] as bool? ?? false,
|
isCorrect: json['isCorrect'] as bool? ?? false,
|
||||||
isAnswered: json['isAnswered'] as bool? ?? false,
|
isAnswered: json['isAnswered'] as bool? ?? false,
|
||||||
|
|
@ -131,6 +143,7 @@ SimpleTestState _$SimpleTestStateFromJson(Map<String, dynamic> json) =>
|
||||||
Map<String, dynamic> _$SimpleTestStateToJson(SimpleTestState instance) =>
|
Map<String, dynamic> _$SimpleTestStateToJson(SimpleTestState instance) =>
|
||||||
<String, dynamic>{
|
<String, dynamic>{
|
||||||
'testType': _$TestQuestionTypeEnumMap[instance.testType]!,
|
'testType': _$TestQuestionTypeEnumMap[instance.testType]!,
|
||||||
|
'word': instance.word,
|
||||||
'answer': instance.answer,
|
'answer': instance.answer,
|
||||||
'isCorrect': instance.isCorrect,
|
'isCorrect': instance.isCorrect,
|
||||||
'isAnswered': instance.isAnswered,
|
'isAnswered': instance.isAnswered,
|
||||||
|
|
|
||||||
|
|
@ -9,11 +9,14 @@ class TestQuestionState {
|
||||||
|
|
||||||
bool get isCorrect => false;
|
bool get isCorrect => false;
|
||||||
|
|
||||||
|
// isAnswered == not skipped by scrolling
|
||||||
final bool isAnswered;
|
final bool isAnswered;
|
||||||
final bool isSkipped;
|
final bool isSkipped;
|
||||||
|
final String word;
|
||||||
|
|
||||||
const TestQuestionState({
|
const TestQuestionState({
|
||||||
required this.testType,
|
required this.testType,
|
||||||
|
required this.word,
|
||||||
this.isAnswered = false,
|
this.isAnswered = false,
|
||||||
this.isSkipped = false,
|
this.isSkipped = false,
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,8 @@ part of 'test_question_state.dart';
|
||||||
abstract class _$TestQuestionStateCWProxy {
|
abstract class _$TestQuestionStateCWProxy {
|
||||||
TestQuestionState testType(TestQuestionType testType);
|
TestQuestionState testType(TestQuestionType testType);
|
||||||
|
|
||||||
|
TestQuestionState word(String word);
|
||||||
|
|
||||||
TestQuestionState isAnswered(bool isAnswered);
|
TestQuestionState isAnswered(bool isAnswered);
|
||||||
|
|
||||||
TestQuestionState isSkipped(bool isSkipped);
|
TestQuestionState isSkipped(bool isSkipped);
|
||||||
|
|
@ -21,6 +23,7 @@ abstract class _$TestQuestionStateCWProxy {
|
||||||
/// ````
|
/// ````
|
||||||
TestQuestionState call({
|
TestQuestionState call({
|
||||||
TestQuestionType? testType,
|
TestQuestionType? testType,
|
||||||
|
String? word,
|
||||||
bool? isAnswered,
|
bool? isAnswered,
|
||||||
bool? isSkipped,
|
bool? isSkipped,
|
||||||
});
|
});
|
||||||
|
|
@ -36,6 +39,9 @@ class _$TestQuestionStateCWProxyImpl implements _$TestQuestionStateCWProxy {
|
||||||
TestQuestionState testType(TestQuestionType testType) =>
|
TestQuestionState testType(TestQuestionType testType) =>
|
||||||
this(testType: testType);
|
this(testType: testType);
|
||||||
|
|
||||||
|
@override
|
||||||
|
TestQuestionState word(String word) => this(word: word);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
TestQuestionState isAnswered(bool isAnswered) => this(isAnswered: isAnswered);
|
TestQuestionState isAnswered(bool isAnswered) => this(isAnswered: isAnswered);
|
||||||
|
|
||||||
|
|
@ -52,6 +58,7 @@ class _$TestQuestionStateCWProxyImpl implements _$TestQuestionStateCWProxy {
|
||||||
/// ````
|
/// ````
|
||||||
TestQuestionState call({
|
TestQuestionState call({
|
||||||
Object? testType = const $CopyWithPlaceholder(),
|
Object? testType = const $CopyWithPlaceholder(),
|
||||||
|
Object? word = const $CopyWithPlaceholder(),
|
||||||
Object? isAnswered = const $CopyWithPlaceholder(),
|
Object? isAnswered = const $CopyWithPlaceholder(),
|
||||||
Object? isSkipped = const $CopyWithPlaceholder(),
|
Object? isSkipped = const $CopyWithPlaceholder(),
|
||||||
}) {
|
}) {
|
||||||
|
|
@ -60,6 +67,10 @@ class _$TestQuestionStateCWProxyImpl implements _$TestQuestionStateCWProxy {
|
||||||
? _value.testType
|
? _value.testType
|
||||||
// ignore: cast_nullable_to_non_nullable
|
// ignore: cast_nullable_to_non_nullable
|
||||||
: testType as TestQuestionType,
|
: testType as TestQuestionType,
|
||||||
|
word: word == const $CopyWithPlaceholder() || word == null
|
||||||
|
? _value.word
|
||||||
|
// ignore: cast_nullable_to_non_nullable
|
||||||
|
: word as String,
|
||||||
isAnswered:
|
isAnswered:
|
||||||
isAnswered == const $CopyWithPlaceholder() || isAnswered == null
|
isAnswered == const $CopyWithPlaceholder() || isAnswered == null
|
||||||
? _value.isAnswered
|
? _value.isAnswered
|
||||||
|
|
|
||||||
|
|
@ -18,10 +18,13 @@ class HorizontalBlockedScrollPhysics extends ScrollPhysics {
|
||||||
/// If [true] it blocks the right movement.
|
/// If [true] it blocks the right movement.
|
||||||
final bool blockRightMovement;
|
final bool blockRightMovement;
|
||||||
|
|
||||||
|
final ValueGetter<bool>? canScroll;
|
||||||
|
|
||||||
const HorizontalBlockedScrollPhysics({
|
const HorizontalBlockedScrollPhysics({
|
||||||
ScrollPhysics? parent,
|
ScrollPhysics? parent,
|
||||||
this.blockLeftMovement = false,
|
this.blockLeftMovement = false,
|
||||||
this.blockRightMovement = false,
|
this.blockRightMovement = false,
|
||||||
|
this.canScroll,
|
||||||
}) : super(parent: parent);
|
}) : super(parent: parent);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -33,20 +36,26 @@ class HorizontalBlockedScrollPhysics extends ScrollPhysics {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get allowUserScrolling => (canScroll == null || canScroll!.call());
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get allowImplicitScrolling => (canScroll == null || canScroll!.call());
|
||||||
|
|
||||||
@override
|
@override
|
||||||
double applyBoundaryConditions(ScrollMetrics position, double value) {
|
double applyBoundaryConditions(ScrollMetrics position, double value) {
|
||||||
assert(() {
|
assert(() {
|
||||||
if (value == position.pixels) {
|
if (value == position.pixels) {
|
||||||
throw FlutterError(
|
throw FlutterError(
|
||||||
'$runtimeType.applyBoundaryConditions() was called redundantly.\n'
|
'$runtimeType.applyBoundaryConditions() was called redundantly.\n'
|
||||||
'The proposed new position, $value, is exactly equal to the current position of the '
|
'The proposed new position, $value, is exactly equal to the current position of the '
|
||||||
'given ${position.runtimeType}, ${position.pixels}.\n'
|
'given ${position.runtimeType}, ${position.pixels}.\n'
|
||||||
'The applyBoundaryConditions method should only be called when the value is '
|
'The applyBoundaryConditions method should only be called when the value is '
|
||||||
'going to actually change the pixels, otherwise it is redundant.\n'
|
'going to actually change the pixels, otherwise it is redundant.\n'
|
||||||
'The physics object in question was:\n'
|
'The physics object in question was:\n'
|
||||||
' $this\n'
|
' $this\n'
|
||||||
'The position object in question was:\n'
|
'The position object in question was:\n'
|
||||||
' $position\n');
|
' $position\n');
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}());
|
}());
|
||||||
|
|
@ -95,6 +104,14 @@ class HorizontalBlockedScrollPhysics extends ScrollPhysics {
|
||||||
|
|
||||||
// We're moving right and we want to block.
|
// We're moving right and we want to block.
|
||||||
if (!isMovingLeft && blockRightMovement && !isPointInScreenLeftRange) {
|
if (!isMovingLeft && blockRightMovement && !isPointInScreenLeftRange) {
|
||||||
|
if (pointInScreen.abs() < delta.abs()) {
|
||||||
|
// fix for strong movements
|
||||||
|
return pointInScreen;
|
||||||
|
}
|
||||||
|
return delta;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (canScroll != null && !canScroll!.call()) {
|
||||||
return delta;
|
return delta;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -138,7 +155,8 @@ class LeftBlockedScrollPhysics extends HorizontalBlockedScrollPhysics {
|
||||||
class RightBlockedScrollPhysics extends HorizontalBlockedScrollPhysics {
|
class RightBlockedScrollPhysics extends HorizontalBlockedScrollPhysics {
|
||||||
const RightBlockedScrollPhysics({
|
const RightBlockedScrollPhysics({
|
||||||
ScrollPhysics? parent,
|
ScrollPhysics? parent,
|
||||||
}) : super(parent: parent, blockRightMovement: true);
|
ValueGetter<bool>? canScroll,
|
||||||
|
}) : super(parent: parent, canScroll: canScroll, blockRightMovement: true);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
RightBlockedScrollPhysics applyTo(ScrollPhysics? ancestor) {
|
RightBlockedScrollPhysics applyTo(ScrollPhysics? ancestor) {
|
||||||
|
|
@ -146,4 +164,4 @@ class RightBlockedScrollPhysics extends HorizontalBlockedScrollPhysics {
|
||||||
parent: buildParent(ancestor),
|
parent: buildParent(ancestor),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
192
lib/features/tests/test_complete_widget.dart
Normal file
192
lib/features/tests/test_complete_widget.dart
Normal file
|
|
@ -0,0 +1,192 @@
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:fl_chart/fl_chart.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
|
import 'package:mnemo_cards/utils/color_helper.dart';
|
||||||
|
import 'package:rxdart/rxdart.dart';
|
||||||
|
|
||||||
|
import '../../widgets/big_back_button.dart';
|
||||||
|
import '../../widgets/horizontal_progress.dart';
|
||||||
|
import 'question_states/test_question_state.dart';
|
||||||
|
|
||||||
|
class TestCompleteWidget extends StatelessWidget {
|
||||||
|
final Iterable<TestQuestionState> states;
|
||||||
|
final Color successColor;
|
||||||
|
|
||||||
|
static final _pieChartKey = GlobalKey<ImplicitlyAnimatedWidgetState>();
|
||||||
|
|
||||||
|
TestCompleteWidget(
|
||||||
|
this.states,
|
||||||
|
this.successColor,
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final total = states.length;
|
||||||
|
final correct = states.where((e) => e.isCorrect).length;
|
||||||
|
final incorrect = states.where((e) => !e.isCorrect && e.isAnswered).length;
|
||||||
|
final skipped = states.where((e) => !e.isAnswered).length;
|
||||||
|
|
||||||
|
final words = states
|
||||||
|
.fold(
|
||||||
|
Map<String, WordStat>(),
|
||||||
|
(map, state) => map
|
||||||
|
..[state.word] = (map[state.word] ?? WordStat.zero(state.word))
|
||||||
|
.inc(state.isCorrect),
|
||||||
|
)
|
||||||
|
.values
|
||||||
|
.toList()
|
||||||
|
..sort((p, n) => p.correctCount.compareTo(n.correctCount));
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
flex: 3,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 12.0),
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
width: MediaQuery.of(context).size.width / 1.8,
|
||||||
|
height: MediaQuery.of(context).size.width / 1.8,
|
||||||
|
padding: const EdgeInsets.all(20.0),
|
||||||
|
child: StreamBuilder<int>(
|
||||||
|
stream: Stream.fromIterable([5])
|
||||||
|
.interval(Duration(milliseconds: 0)),
|
||||||
|
initialData: 0,
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
final a = min(snapshot.requireData / 5.0, 1.0);
|
||||||
|
return PieChart(
|
||||||
|
key: _pieChartKey,
|
||||||
|
swapAnimationDuration: Duration(milliseconds: 600),
|
||||||
|
PieChartData(
|
||||||
|
pieTouchData: PieTouchData(),
|
||||||
|
borderData: FlBorderData(
|
||||||
|
show: false,
|
||||||
|
),
|
||||||
|
sectionsSpace: 0,
|
||||||
|
centerSpaceRadius:
|
||||||
|
MediaQuery.of(context).size.width / 8,
|
||||||
|
sections: [
|
||||||
|
PieChartSectionData(
|
||||||
|
color: successColor,
|
||||||
|
showTitle: false,
|
||||||
|
value: correct.toDouble() * a,
|
||||||
|
badgeWidget: Image.asset(
|
||||||
|
'icons/crown.png',
|
||||||
|
width: 20.w,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
PieChartSectionData(
|
||||||
|
color: successColor.lighten(0.15),
|
||||||
|
showTitle: false,
|
||||||
|
value: incorrect.toDouble() * a,
|
||||||
|
badgeWidget: Image.asset(
|
||||||
|
'icons/skull.png',
|
||||||
|
width: 20.w,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
PieChartSectionData(
|
||||||
|
color: Colors.white,
|
||||||
|
showTitle: false,
|
||||||
|
value: skipped.toDouble(),
|
||||||
|
badgeWidget: Image.asset(
|
||||||
|
'icons/small_circle.png',
|
||||||
|
width: 20.w,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'$correct/$total',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 36,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'1:47',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
height: 0.85,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
flex: 2,
|
||||||
|
child: Container(
|
||||||
|
alignment: Alignment.bottomCenter,
|
||||||
|
child: ListView.builder(
|
||||||
|
shrinkWrap: true,
|
||||||
|
itemCount: words.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final word = words[index];
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
left: 12.0,
|
||||||
|
right: 24,
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'${word.word}',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 24,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Flexible(
|
||||||
|
child: SizedBox(
|
||||||
|
width: MediaQuery.of(context).size.width / 2.5,
|
||||||
|
child: HorizontalProgressWidget(
|
||||||
|
word.correctCount,
|
||||||
|
word.totalCount,
|
||||||
|
successColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const BigBackButton(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class WordStat {
|
||||||
|
String word;
|
||||||
|
int correctCount;
|
||||||
|
int totalCount;
|
||||||
|
|
||||||
|
WordStat(this.word, this.correctCount, this.totalCount);
|
||||||
|
|
||||||
|
factory WordStat.zero(String word) => WordStat(word, 0, 0);
|
||||||
|
|
||||||
|
WordStat inc(bool isCorrect) => isCorrect ? correct(this) : incorrect(this);
|
||||||
|
|
||||||
|
WordStat correct(WordStat stat) => WordStat(
|
||||||
|
stat.word,
|
||||||
|
stat.correctCount + 1,
|
||||||
|
stat.totalCount + 1,
|
||||||
|
);
|
||||||
|
|
||||||
|
WordStat incorrect(WordStat stat) => WordStat(
|
||||||
|
stat.word,
|
||||||
|
stat.correctCount,
|
||||||
|
stat.totalCount + 1,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -4,12 +4,11 @@ import 'dart:math' as math;
|
||||||
import 'package:auto_route/auto_route.dart';
|
import 'package:auto_route/auto_route.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
import 'package:mnemo_cards/features/tests/question_states/test_question_state.dart';
|
|
||||||
import 'package:mnemo_cards_common/mnemo_cards_common.dart';
|
import 'package:mnemo_cards_common/mnemo_cards_common.dart';
|
||||||
import 'package:pie_chart/pie_chart.dart';
|
|
||||||
|
|
||||||
import '../../di/locator.dart';
|
import '../../di/locator.dart';
|
||||||
import 'scroll_physics.dart';
|
import 'scroll_physics.dart';
|
||||||
|
import 'test_complete_widget.dart';
|
||||||
import 'test_progress_widget.dart';
|
import 'test_progress_widget.dart';
|
||||||
import 'test_widgets/input_buttons_test.dart';
|
import 'test_widgets/input_buttons_test.dart';
|
||||||
import 'test_widgets/simple_test.dart';
|
import 'test_widgets/simple_test.dart';
|
||||||
|
|
@ -25,7 +24,6 @@ class TestPage extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _TestPageState extends State<TestPage> {
|
class _TestPageState extends State<TestPage> {
|
||||||
|
|
||||||
PageController get pageController => locator.testManager.pageController;
|
PageController get pageController => locator.testManager.pageController;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -44,13 +42,11 @@ class _TestPageState extends State<TestPage> {
|
||||||
|
|
||||||
void _pageScrollListener() {
|
void _pageScrollListener() {
|
||||||
final index = pageController.page?.floor() ?? 0;
|
final index = pageController.page?.floor() ?? 0;
|
||||||
if (index > 0) {
|
if (index > 0 && index <= locator.testManager.questions.length) {
|
||||||
final prevId =
|
final prevId = locator.testManager.questions[index - 1].id!;
|
||||||
locator.testManager.questions[index - 1].id!;
|
final state = locator.testManager.getState(prevId);
|
||||||
final state =
|
|
||||||
locator.testManager.getState(prevId);
|
|
||||||
if (state?.isAnswered == false) {
|
if (state?.isAnswered == false) {
|
||||||
log('Question skipped');
|
// log('Question skipped');
|
||||||
locator.testManager.skip(prevId);
|
locator.testManager.skip(prevId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -90,90 +86,18 @@ class _TestPageState extends State<TestPage> {
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: PageView.builder(
|
child: PageView.builder(
|
||||||
physics: RightBlockedScrollPhysics(),
|
physics: RightBlockedScrollPhysics(
|
||||||
|
canScroll: _canScroll,
|
||||||
|
),
|
||||||
controller: locator.testManager.pageController,
|
controller: locator.testManager.pageController,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
if (index > locator.testManager.questions.length) {
|
if (index > locator.testManager.questions.length) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (index == locator.testManager.questions.length) {
|
if (index == locator.testManager.questions.length) {
|
||||||
return Column(
|
return TestCompleteWidget(
|
||||||
children: [
|
locator.testManager.states,
|
||||||
Expanded(
|
locator.testManager.color,
|
||||||
child: PieChart(
|
|
||||||
dataMap: {
|
|
||||||
'Правильно': locator.testManager.states
|
|
||||||
.where((e) => e.isCorrect)
|
|
||||||
.length
|
|
||||||
.toDouble(),
|
|
||||||
'Ошибка': locator.testManager.states
|
|
||||||
.where((e) =>
|
|
||||||
!e.isCorrect && e.isAnswered)
|
|
||||||
.length
|
|
||||||
.toDouble(),
|
|
||||||
'Пропущено': locator.testManager.states
|
|
||||||
.where((e) => !e.isAnswered)
|
|
||||||
.length
|
|
||||||
.toDouble(),
|
|
||||||
},
|
|
||||||
animationDuration:
|
|
||||||
Duration(milliseconds: 800),
|
|
||||||
chartLegendSpacing: 32,
|
|
||||||
chartRadius:
|
|
||||||
MediaQuery.of(context).size.width /
|
|
||||||
3.2,
|
|
||||||
colorList: [
|
|
||||||
Colors.lightGreenAccent[200]!,
|
|
||||||
Colors.redAccent[100]!,
|
|
||||||
Colors.white,
|
|
||||||
],
|
|
||||||
initialAngleInDegree: 0,
|
|
||||||
chartType: ChartType.ring,
|
|
||||||
ringStrokeWidth: 32,
|
|
||||||
legendOptions: LegendOptions(
|
|
||||||
showLegendsInRow: false,
|
|
||||||
legendPosition: LegendPosition.right,
|
|
||||||
showLegends: true,
|
|
||||||
legendShape: BoxShape.circle,
|
|
||||||
legendTextStyle: TextStyle(
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
chartValuesOptions: ChartValuesOptions(
|
|
||||||
showChartValueBackground: false,
|
|
||||||
showChartValues: true,
|
|
||||||
showChartValuesOutside: false,
|
|
||||||
decimalPlaces: 0,
|
|
||||||
chartValueStyle: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.titleMedium!,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Center(
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(8.0),
|
|
||||||
child: InkWell(
|
|
||||||
onTap: () {
|
|
||||||
AutoRouter.of(context).maybePop();
|
|
||||||
},
|
|
||||||
child: Container(
|
|
||||||
alignment: Alignment.center,
|
|
||||||
padding: const EdgeInsets.all(8.0),
|
|
||||||
color: locator.testManager.color,
|
|
||||||
height: 60,
|
|
||||||
child: FittedBox(
|
|
||||||
child: Text(
|
|
||||||
'В меню',
|
|
||||||
style: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.titleMedium,
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -207,6 +131,12 @@ class _TestPageState extends State<TestPage> {
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool _canScroll() {
|
||||||
|
return locator.testManager.pageController.hasClients &&
|
||||||
|
locator.testManager.pageController.page !=
|
||||||
|
locator.testManager.questions.length;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _LifeWidget extends StatelessWidget {
|
class _LifeWidget extends StatelessWidget {
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'dart:async';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:auto_route/auto_route.dart';
|
import 'package:auto_route/auto_route.dart';
|
||||||
|
|
@ -24,21 +25,30 @@ class _TestProgressWidgetState extends State<TestProgressWidget> {
|
||||||
|
|
||||||
Stream<Duration>? timer;
|
Stream<Duration>? timer;
|
||||||
|
|
||||||
|
Timer? _updateTimer;
|
||||||
|
|
||||||
|
void _updateDataListener() {
|
||||||
|
if (_updateTimer == null || !_updateTimer!.isActive) {
|
||||||
|
_updateTimer = Timer(Duration(milliseconds: 100), _updateData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void _updateData() {
|
void _updateData() {
|
||||||
results = <Result>[
|
results = <Result>[
|
||||||
Result.correct,
|
Result.correct,
|
||||||
];
|
];
|
||||||
length = locator.testManager.questions.length + 1;
|
length = locator.testManager.questions.length + 1;
|
||||||
|
final pageController = locator.testManager.pageController;
|
||||||
for (final state in locator.testManager.states) {
|
for (final state in locator.testManager.states) {
|
||||||
if (state.isAnswered) {
|
if (state.isAnswered) {
|
||||||
if (state.isCorrect) {
|
if (state.isCorrect) {
|
||||||
results.add(Result.correct);
|
results.add(Result.correct);
|
||||||
} else {
|
} else {
|
||||||
// results.add(Result.wrong);
|
// results.add(Result.wrong);
|
||||||
results.add(Result.not_visited);
|
results.add(Result.wrong);
|
||||||
}
|
}
|
||||||
} else if (state.isSkipped) {
|
} else if (state.isSkipped) {
|
||||||
results.add(Result.correct);
|
results.add(Result.skiped);
|
||||||
} else {
|
} else {
|
||||||
results.add(Result.not_visited);
|
results.add(Result.not_visited);
|
||||||
}
|
}
|
||||||
|
|
@ -51,8 +61,10 @@ class _TestProgressWidgetState extends State<TestProgressWidget> {
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
locator.testManager.removeListener(_updateData);
|
// locator.testManager.removeListener(_updateData);
|
||||||
locator.testManager.addListener(_updateData);
|
// locator.testManager.addListener(_updateData);
|
||||||
|
locator.testManager.pageController.removeListener(_updateDataListener);
|
||||||
|
locator.testManager.pageController.addListener(_updateDataListener);
|
||||||
timer = Stream.periodic(
|
timer = Stream.periodic(
|
||||||
Duration(seconds: 1),
|
Duration(seconds: 1),
|
||||||
(tick) => Duration(seconds: 300 - tick - 1),
|
(tick) => Duration(seconds: 300 - tick - 1),
|
||||||
|
|
|
||||||
|
|
@ -55,9 +55,15 @@ class TestHolder {
|
||||||
for (final question in questions) {
|
for (final question in questions) {
|
||||||
switch (question.questionType) {
|
switch (question.questionType) {
|
||||||
case TestQuestionType.simple:
|
case TestQuestionType.simple:
|
||||||
stateQuestions[question.id!] = SimpleTestState(seed: seed);
|
stateQuestions[question.id!] = SimpleTestState(
|
||||||
|
seed: seed,
|
||||||
|
word: question.word,
|
||||||
|
);
|
||||||
case TestQuestionType.input_buttons:
|
case TestQuestionType.input_buttons:
|
||||||
stateQuestions[question.id!] = InputButtonsTestState(seed: seed);
|
stateQuestions[question.id!] = InputButtonsTestState(
|
||||||
|
seed: seed,
|
||||||
|
word: question.word,
|
||||||
|
);
|
||||||
case TestQuestionType.matrix:
|
case TestQuestionType.matrix:
|
||||||
// TODO: Handle this case.
|
// TODO: Handle this case.
|
||||||
case TestQuestionType.match:
|
case TestQuestionType.match:
|
||||||
|
|
|
||||||
|
|
@ -58,11 +58,12 @@ class SimpleTestWidget extends StatelessWidget {
|
||||||
answer: answer,
|
answer: answer,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
// only setting isCorrect once
|
||||||
manager.setState(
|
manager.setState(
|
||||||
model.id!,
|
model.id!,
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
isAnswered: true,
|
isAnswered: true,
|
||||||
isCorrect: isCorrect,
|
isCorrect: state.isAnswered ? state.isCorrect : isCorrect,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ import '../admin/all_users.dart';
|
||||||
import '../di/locator.dart';
|
import '../di/locator.dart';
|
||||||
import '../features/analytics/analytics.dart';
|
import '../features/analytics/analytics.dart';
|
||||||
import '../theme/themes.dart';
|
import '../theme/themes.dart';
|
||||||
|
import '../widgets/big_back_button.dart';
|
||||||
import '../widgets/shared_pref_button.dart';
|
import '../widgets/shared_pref_button.dart';
|
||||||
import '../widgets/text_button.dart';
|
import '../widgets/text_button.dart';
|
||||||
|
|
||||||
|
|
@ -178,31 +179,7 @@ class ProfilePage extends StatelessWidget {
|
||||||
// },
|
// },
|
||||||
// ),
|
// ),
|
||||||
// ),
|
// ),
|
||||||
Padding(
|
const BigBackButton(),
|
||||||
padding:
|
|
||||||
EdgeInsets.symmetric(horizontal: 10.0.w, vertical: 10.0.h),
|
|
||||||
child: InkWell(
|
|
||||||
onTap: AutoRouter.of(context).maybePop,
|
|
||||||
child: Container(
|
|
||||||
height: 90.h,
|
|
||||||
alignment: Alignment.center,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.white,
|
|
||||||
border: Border.all(color: borderGray),
|
|
||||||
borderRadius: BorderRadius.circular(16.0),
|
|
||||||
),
|
|
||||||
padding: EdgeInsets.all(10.0.w),
|
|
||||||
child: Text(
|
|
||||||
'Назад',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 25.sp,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
height: 0.85,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
38
lib/widgets/big_back_button.dart
Normal file
38
lib/widgets/big_back_button.dart
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
|
import 'package:mnemo_cards/main.dart';
|
||||||
|
|
||||||
|
import '../theme/themes.dart';
|
||||||
|
|
||||||
|
class BigBackButton extends StatelessWidget {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Padding(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 10.0.w, vertical: 10.0.h),
|
||||||
|
child: InkWell(
|
||||||
|
onTap: appRouter.maybePop,
|
||||||
|
child: Container(
|
||||||
|
height: 90.h,
|
||||||
|
alignment: Alignment.center,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
border: Border.all(color: borderGray),
|
||||||
|
borderRadius: BorderRadius.circular(16.0),
|
||||||
|
),
|
||||||
|
padding: EdgeInsets.all(10.0.w),
|
||||||
|
child: Text(
|
||||||
|
'Назад',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 25.sp,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
height: 0.85,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const BigBackButton();
|
||||||
|
}
|
||||||
31
lib/widgets/horizontal_progress.dart
Normal file
31
lib/widgets/horizontal_progress.dart
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
|
|
||||||
|
import '../theme/themes.dart';
|
||||||
|
|
||||||
|
class HorizontalProgressWidget extends StatelessWidget {
|
||||||
|
final int value;
|
||||||
|
final int length;
|
||||||
|
final Color color;
|
||||||
|
|
||||||
|
HorizontalProgressWidget(this.value, this.length, this.color);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(16.h),
|
||||||
|
child: Container(
|
||||||
|
height: 16.h,
|
||||||
|
color: white,
|
||||||
|
child: AnimatedFractionallySizedBox(
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
widthFactor: length == 0 ? 0 : value.toDouble() / length,
|
||||||
|
duration: Duration(milliseconds: 600),
|
||||||
|
child: Container(
|
||||||
|
color: color,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
24
pubspec.lock
24
pubspec.lock
|
|
@ -321,6 +321,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.2"
|
version: "1.0.2"
|
||||||
|
equatable:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: equatable
|
||||||
|
sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.5"
|
||||||
fake_async:
|
fake_async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -425,6 +433,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.0"
|
version: "1.1.0"
|
||||||
|
fl_chart:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: fl_chart
|
||||||
|
sha256: d0f0d49112f2f4b192481c16d05b6418bd7820e021e265a3c22db98acf7ed7fb
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.68.0"
|
||||||
flutter:
|
flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description: flutter
|
description: flutter
|
||||||
|
|
@ -919,14 +935,6 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.0.2"
|
version: "6.0.2"
|
||||||
pie_chart:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: pie_chart
|
|
||||||
sha256: "58e6a46999ac938bfa1c3e5be414d6e149f037647197dca03ba3614324c12c82"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "5.4.0"
|
|
||||||
platform:
|
platform:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ dependencies:
|
||||||
reorderables: ^0.6.0
|
reorderables: ^0.6.0
|
||||||
story: ^1.1.0
|
story: ^1.1.0
|
||||||
flutter_screenutil: ^5.9.0
|
flutter_screenutil: ^5.9.0
|
||||||
pie_chart: ^5.4.0
|
fl_chart: ^0.68.0
|
||||||
yookassa_client: ^1.0.2
|
yookassa_client: ^1.0.2
|
||||||
# The following adds the Cupertino Icons font to your application.
|
# The following adds the Cupertino Icons font to your application.
|
||||||
# Use with the CupertinoIcons class for iOS style icons.
|
# Use with the CupertinoIcons class for iOS style icons.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue