Skip to content

Instantly share code, notes, and snippets.

@stargazing-dino
Created June 23, 2021 20:38
Show Gist options
  • Save stargazing-dino/c75c4f7e48f01ab7f72d325d2db378c0 to your computer and use it in GitHub Desktop.
Save stargazing-dino/c75c4f7e48f01ab7f72d325d2db378c0 to your computer and use it in GitHub Desktop.
simulatorCard
import 'package:async_button_builder/async_button_builder.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_utils/flutter_utils.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:nremt_paramedic_prep/data/profile/profile.dart';
import 'package:nremt_paramedic_prep/models/doc/doc.dart';
import 'package:nremt_paramedic_prep/models/test/test.dart';
import 'package:nremt_paramedic_prep/providers/categories.dart';
import 'package:nremt_paramedic_prep/providers/question_count.dart';
import 'package:nremt_paramedic_prep/providers/tests.dart';
import 'package:nremt_paramedic_prep/utils/combine_async.dart';
import 'package:nremt_paramedic_prep/utils/default_error.dart';
import 'package:nremt_paramedic_prep/utils/random_question_ids.dart';
import 'package:nremt_paramedic_prep/utils/shimmer_colors.dart';
import 'package:nremt_paramedic_prep/utils/tests.dart';
import 'package:nremt_paramedic_prep/widgets/test_progress.dart';
import 'package:shimmer/shimmer.dart';
import 'package:typicons_flutter/typicons_flutter.dart';
class SimulatorCard extends HookWidget {
final String title;
final Doc<Profile> profileDoc;
final void Function(MaybeDoc<Test> test, bool shuffle) onTap;
final void Function(MaybeDoc<Test> test, bool shuffle) onCreate;
const SimulatorCard({
Key? key,
required this.title,
required this.profileDoc,
required this.onTap,
required this.onCreate,
}) : super(key: key);
static final _testKey = 'exam';
static final _maxTestLength = 100.0;
@override
Widget build(BuildContext context) {
final testLength = useState(50.0);
final theme = Theme.of(context);
final startColor = theme.brightness == Brightness.dark
? theme.dividerColor.withOpacity(.04)
: theme.primaryColor.withOpacity(.04);
final endColor = theme.cardColor.withOpacity(.3);
final profile = profileDoc.data;
final slidingTile = ListTile(
dense: true,
leading: ConstrainedBox(
constraints: BoxConstraints(minWidth: 100.0),
child: Text(
'${testLength.value.toInt()} Questions:'.padRight(15),
style: theme.textTheme.subtitle2,
),
),
title: Slider(
min: 10,
max: _maxTestLength,
divisions: (_maxTestLength - 10) ~/ 5,
value: testLength.value,
onChanged: (value) => testLength.value = value,
label: '${testLength.value.toInt()}',
),
);
return combineAsync3(
useProvider(questionCountDocProvider),
useProvider(testDocsProvider(profileDoc)),
useProvider(questionCategoryDocsProvider),
).when(
data: (tupleResults) {
final questionCountDoc = tupleResults.item1;
final questionCount = questionCountDoc.data;
final testDocs = tupleResults.item2;
final categoryDocs = tupleResults.item3;
final challengeTestDocs = testDocsForKey(testDocs, _testKey);
final filteredCategoryDocs = categoryDocs
.where((doc) => doc.data.simulatorPercent != 0)
.toList();
List<String> _questionIDsForNewTest(
double testLength,
) {
final questionIDs = <String>[];
final maxAmount = testLength.round();
for (final categoryDoc in categoryDocs) {
final amountInCategory =
(testLength * categoryDoc.data.simulatorPercent).round();
questionIDs.addAll(
randomQuestionIDs(
questionToCategory: questionCount.questionToCategory,
currentQuestionIDs: questionIDs,
category: categoryDoc.id,
amount: amountInCategory,
maxAmount: maxAmount,
),
);
}
return questionIDs;
}
return HookBuilder(
builder: (context) {
final questionIDs = useState(<String>[]);
final testDoc = useMemoized(
() {
if (challengeTestDocs.isNotEmpty) {
final _testDoc = challengeTestDocs.last.copyWith(
data: Test.alignWithQuestions(
challengeTestDocs.last.data,
questionIDs.value,
),
);
questionIDs.value =
_testDoc.data.questionsAndAnswers.keys.toList();
return _testDoc;
} else {
questionIDs.value = _questionIDsForNewTest(testLength.value);
return UnsavedDoc(
data: Test.alignWithQuestions(
Test.empty(
title: title,
key: _testKey,
questionIDs: questionIDs.value,
),
questionIDs.value,
),
);
}
},
[testLength.value],
);
// final test = useState<Test?>(null);
// final testReference = useState<DocumentReference<Test>?>(null);
final test = testDoc.data;
return Material(
elevation: 0.0,
color: theme.cardColor,
borderRadius: BorderRadius.circular(4.0),
child: Column(
children: <Widget>[
Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
if (theme.brightness == Brightness.light) endColor,
startColor,
endColor,
],
),
),
child: ListTile(
onTap: profile.isNotPremium
? null
: () => onTap(testDoc, true),
title: Text(
title,
style: theme.textTheme.headline6
?.copyWith(fontWeight: FontWeight.w600),
),
trailing: profile.isNotPremium
? const Icon(Typicons.lock_closed_outline)
: TestProgress(
percentDone: test.percentDone,
testDocs: challengeTestDocs,
),
),
),
Padding(
padding: const EdgeInsets.symmetric(
horizontal: kSize,
vertical: kSizeSm,
),
child: DefaultTextStyle(
style: theme.textTheme.caption?.copyWith(height: 1.6) ??
TextStyle(),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Text(
'Tests are composed of multiple choice questions from the following categories:',
style: theme.textTheme.caption?.copyWith(
fontWeight: FontWeight.w500,
fontSize: 14.0,
),
),
const SizedBox(height: kSizeSm),
IntrinsicHeight(
child: Row(
children: [
Flexible(
child: Column(
crossAxisAlignment:
CrossAxisAlignment.stretch,
children: filteredCategoryDocs
.take(filteredCategoryDocs.length ~/ 2)
.map(
(categoryDoc) {
final category = categoryDoc.data;
return Text(
'${category.simulatorPercent * 100}%'
' ${category.title}',
);
},
).toList(),
),
),
const SizedBox(width: kSize),
Flexible(
child: Column(
crossAxisAlignment:
CrossAxisAlignment.stretch,
mainAxisSize: MainAxisSize.max,
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: filteredCategoryDocs
.sublist(
filteredCategoryDocs.length ~/ 2,
filteredCategoryDocs.length,
)
.map(
(categoryDoc) {
final category = categoryDoc.data;
return Text(
'${category.simulatorPercent * 100}%'
' ${category.title}',
);
},
).toList(),
),
),
],
),
),
],
),
),
),
if (challengeTestDocs.isEmpty)
slidingTile
else
Theme(
data: theme.copyWith(dividerColor: Colors.transparent),
child: ExpansionTile(
title: Text('Generate New Test'),
expandedCrossAxisAlignment: CrossAxisAlignment.stretch,
children: [
slidingTile,
Padding(
padding: const EdgeInsets.symmetric(
horizontal: kSize,
),
child: AsyncButtonBuilder(
onPressed: profile.isNotPremium
? null
: () async {
await testDoc.when(
(reference, data) async {
await reference.delete();
},
unsaved: (data) {},
);
questionIDs.value =
_questionIDsForNewTest(
testLength.value,
);
final newTestDoc = UnsavedDoc(
data: Test.empty(
title: 'REMAC Simulator',
key: _testKey,
questionIDs: questionIDs.value,
),
);
onCreate(newTestDoc, true);
},
builder: (context, child, callback, _) {
return OutlinedButton.icon(
icon: child,
label: const Text('CREATE'),
onPressed: callback,
);
},
child: profile.isNotPremium
? Icon(Icons.lock)
: Icon(Icons.navigate_next),
),
)
],
),
),
],
),
);
},
);
},
loading: () => Shimmer.fromColors(
baseColor: getBaseColor(theme),
highlightColor: getHighlightColor(theme),
child: Material(
borderRadius: BorderRadius.circular(4.0),
child: SizedBox(height: 224.0),
),
),
error: defaultError,
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment