Skip to content

Commit

Permalink
[Presentation] Added a characters per region chart + dialog
Browse files Browse the repository at this point in the history
  • Loading branch information
Wolfteam committed May 23, 2022
1 parent 5946d1e commit 0c47420
Show file tree
Hide file tree
Showing 9 changed files with 216 additions and 95 deletions.
10 changes: 10 additions & 0 deletions lib/injection.dart
Expand Up @@ -194,6 +194,16 @@ class Injection {
return ItemsAscensionStatsBloc(genshinService);
}

static ChartRegionsBloc get chartRegionsBloc {
final genshinService = getIt<GenshinService>();
return ChartRegionsBloc(genshinService);
}

static CharactersPerRegionBloc get charactersPerRegionBloc {
final genshinService = getIt<GenshinService>();
return CharactersPerRegionBloc(genshinService);
}

//TODO: USE THIS PROP
// static CalculatorAscMaterialsItemBloc get calculatorAscMaterialsItemBloc {
// final genshinService = getIt<GenshinService>();
Expand Down
3 changes: 2 additions & 1 deletion lib/l10n/intl_en.arb
Expand Up @@ -444,5 +444,6 @@
"firstPage": "First page",
"previousPage": "Previous page",
"nextPage": "Next page",
"lastPage": "Last page"
"lastPage": "Last page",
"regions": "Regions"
}
42 changes: 37 additions & 5 deletions lib/presentation/charts/charts_page.dart
Expand Up @@ -12,13 +12,14 @@ import 'package:shiori/generated/l10n.dart';
import 'package:shiori/injection.dart';
import 'package:shiori/presentation/banner_history/widgets/version_details_dialog.dart';
import 'package:shiori/presentation/character/character_page.dart';
import 'package:shiori/presentation/charts/widgets/birthdays_per_month_dialog.dart';
import 'package:shiori/presentation/charts/widgets/chart_card.dart';
import 'package:shiori/presentation/charts/widgets/chart_legend.dart';
import 'package:shiori/presentation/charts/widgets/horizontal_bar_chart.dart';
import 'package:shiori/presentation/charts/widgets/items_ascension_stats_dialog.dart';
import 'package:shiori/presentation/charts/widgets/pie_chart.dart';
import 'package:shiori/presentation/charts/widgets/vertical_bar_chart.dart';
import 'package:shiori/presentation/shared/dialogs/birthdays_per_month_dialog.dart';
import 'package:shiori/presentation/shared/dialogs/characters_per_region_dialog.dart';
import 'package:shiori/presentation/shared/dialogs/items_ascension_stats_dialog.dart';
import 'package:shiori/presentation/shared/extensions/element_type_extensions.dart';
import 'package:shiori/presentation/shared/extensions/i18n_extensions.dart';
import 'package:shiori/presentation/shared/loading.dart';
Expand Down Expand Up @@ -81,6 +82,9 @@ class ChartsPage extends StatelessWidget {
),
),
),
BlocProvider<ChartRegionsBloc>(
create: (context) => Injection.chartRegionsBloc..add(const ChartRegionsEvent.init()),
),
],
child: Scaffold(
appBar: AppBar(
Expand Down Expand Up @@ -275,13 +279,14 @@ class ChartsPage extends StatelessWidget {
width: mq.size.width,
height: _defaultChartHeight,
title: s.mostAndLeastRepeated,
titleMargin: const EdgeInsets.only(bottom: 20),
child: BlocBuilder<ChartBirthdaysBloc, ChartBirthdaysState>(
builder: (context, state) => state.maybeMap(
loaded: (state) {
if (state.birthdays.isEmpty) {
return NothingFoundColumn(msg: s.nothingToShow);
}
final maxYValueForBirthdays = state.birthdays.map((e) => e.items.length).reduce(max).toDouble() + 1;
final maxYValueForBirthdays = state.birthdays.map((e) => e.items.length).reduce(max).toDouble();
return VerticalBarChart(
items: state.birthdays
.mapIndex((e, i) => VerticalBarDataModel(i, theme.colorScheme.primary, e.month, e.items.length.toDouble()))
Expand All @@ -303,7 +308,6 @@ class ChartsPage extends StatelessWidget {
),
),
//TODO: CHAR / WEAPON SCALING
//TODO: REGIONS (VERTICAL BAR)
//TODO: GENDER (VERTICAL BAR) ?
//TODO: CHAR ROLE (VERTICAL BAR)
//TODO: CHARACTER MOST USED WEAPON TYPES (VERTICAL BAR)
Expand All @@ -315,6 +319,7 @@ class ChartsPage extends StatelessWidget {
width: mq.size.width,
height: _defaultChartHeight,
title: s.mostAndLeastRepeated,
titleMargin: const EdgeInsets.only(bottom: 20),
bottom: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Expand Down Expand Up @@ -363,7 +368,7 @@ class ChartsPage extends StatelessWidget {
items: state.ascensionStats
.mapIndex((e, i) => VerticalBarDataModel(i, theme.colorScheme.primary, e.type.index, e.quantity.toDouble()))
.toList(),
maxY: state.maxCount + 1,
maxY: state.maxCount.toDouble(),
interval: (state.maxCount * 0.2).roundToDouble(),
tooltipColor: tooltipColor,
getBottomText: (value) => s.translateStatTypeWithoutValue(StatType.values[value.toInt()]),
Expand All @@ -378,6 +383,33 @@ class ChartsPage extends StatelessWidget {
orElse: () => const Loading(useScaffold: false),
),
),
Text(s.regions, style: theme.textTheme.headline5),
ChartCard(
width: mq.size.width,
height: _defaultChartHeight,
title: s.mostAndLeastRepeated,
titleMargin: const EdgeInsets.only(bottom: 20),
child: BlocBuilder<ChartRegionsBloc, ChartRegionsState>(
builder: (context, state) => state.maybeMap(
loaded: (state) => VerticalBarChart(
items: state.items
.mapIndex((e, i) => VerticalBarDataModel(i, theme.colorScheme.primary, e.regionType.index, e.quantity.toDouble()))
.toList(),
maxY: state.maxCount.toDouble(),
interval: (state.maxCount * 0.2).roundToDouble(),
tooltipColor: tooltipColor,
getBottomText: (value) => s.translateRegionType(state.items[value.toInt()].regionType),
getLeftText: (value) => value.toInt().toString(),
rotateBottomText: true,
onBarChartTap: (index) => showDialog(
context: context,
builder: (_) => CharactersPerRegionDialog(regionType: state.items[index].regionType),
),
),
orElse: () => const Loading(useScaffold: false),
),
),
)
],
),
),
Expand Down
4 changes: 3 additions & 1 deletion lib/presentation/charts/widgets/chart_card.dart
Expand Up @@ -7,6 +7,7 @@ class ChartCard extends StatelessWidget {
final Widget? bottom;
final double height;
final double width;
final EdgeInsets titleMargin;

const ChartCard({
Key? key,
Expand All @@ -15,6 +16,7 @@ class ChartCard extends StatelessWidget {
required this.width,
required this.height,
this.bottom,
this.titleMargin = const EdgeInsets.only(bottom: 10),
}) : super(key: key);

@override
Expand All @@ -32,7 +34,7 @@ class ChartCard extends StatelessWidget {
mainAxisSize: MainAxisSize.min,
children: [
Container(
margin: const EdgeInsets.only(bottom: 10),
margin: titleMargin,
child: Text(
title,
style: theme.textTheme.headline6,
Expand Down
@@ -1,14 +1,14 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:shiori/application/birthdays_per_month/birthdays_per_month_bloc.dart';
import 'package:shiori/domain/enums/enums.dart';
import 'package:shiori/domain/models/models.dart';
import 'package:shiori/domain/utils/date_utils.dart' as date_utils;
import 'package:shiori/generated/l10n.dart';
import 'package:shiori/injection.dart';
import 'package:shiori/presentation/shared/dialogs/dialog_list_item_row.dart';
import 'package:shiori/presentation/shared/extensions/media_query_extensions.dart';
import 'package:shiori/presentation/shared/images/circle_character.dart';
import 'package:shiori/presentation/shared/loading.dart';
import 'package:shiori/presentation/shared/styles.dart';

class BirthdaysPerMonthDialog extends StatelessWidget {
final int month;
Expand Down Expand Up @@ -53,7 +53,16 @@ class BirthdaysPerMonthDialog extends StatelessWidget {
width: mq.getWidthForDialogs(),
child: ListView.builder(
itemCount: state.characters.length,
itemBuilder: (context, index) => _Row(character: state.characters[index]),
itemBuilder: (context, index) {
final char = state.characters[index];
return DialogListItemRow(
itemType: ItemType.character,
itemKey: char.key,
image: char.image,
name: char.name,
getRowEndWidget: (_) => _RowEndColumn(character: char),
);
},
),
),
orElse: () => const Loading(useScaffold: false),
Expand All @@ -64,10 +73,10 @@ class BirthdaysPerMonthDialog extends StatelessWidget {
}
}

class _Row extends StatelessWidget {
class _RowEndColumn extends StatelessWidget {
final CharacterBirthdayModel character;

const _Row({
const _RowEndColumn({
Key? key,
required this.character,
}) : super(key: key);
Expand All @@ -77,48 +86,28 @@ class _Row extends StatelessWidget {
final s = S.of(context);
final theme = Theme.of(context);
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
character.name,
overflow: TextOverflow.ellipsis,
style: theme.textTheme.subtitle1,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
CircleCharacter(
itemKey: character.key,
image: character.image,
radius: 40,
Text(
character.birthdayString,
overflow: TextOverflow.ellipsis,
style: theme.textTheme.caption,
),
Expanded(
child: Padding(
padding: Styles.edgeInsetHorizontal16,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
character.name,
overflow: TextOverflow.ellipsis,
style: theme.textTheme.subtitle1,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
character.birthdayString,
overflow: TextOverflow.ellipsis,
style: theme.textTheme.caption,
),
Text(
character.daysUntilBirthday > 0 ? s.inXDays(character.daysUntilBirthday) : s.today,
overflow: TextOverflow.ellipsis,
style: theme.textTheme.caption!.copyWith(color: theme.colorScheme.primary, fontWeight: FontWeight.bold),
),
],
),
],
),
),
Text(
character.daysUntilBirthday > 0 ? s.inXDays(character.daysUntilBirthday) : s.today,
overflow: TextOverflow.ellipsis,
style: theme.textTheme.caption!.copyWith(color: theme.colorScheme.primary, fontWeight: FontWeight.bold),
),
],
),
const Divider(),
],
);
}
Expand Down
67 changes: 67 additions & 0 deletions lib/presentation/shared/dialogs/characters_per_region_dialog.dart
@@ -0,0 +1,67 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:shiori/application/bloc.dart';
import 'package:shiori/domain/enums/enums.dart';
import 'package:shiori/generated/l10n.dart';
import 'package:shiori/injection.dart';
import 'package:shiori/presentation/shared/dialogs/dialog_list_item_row.dart';
import 'package:shiori/presentation/shared/extensions/i18n_extensions.dart';
import 'package:shiori/presentation/shared/extensions/media_query_extensions.dart';
import 'package:shiori/presentation/shared/loading.dart';
import 'package:shiori/presentation/shared/nothing_found_column.dart';

class CharactersPerRegionDialog extends StatelessWidget {
final RegionType regionType;

const CharactersPerRegionDialog({
Key? key,
required this.regionType,
}) : super(key: key);

@override
Widget build(BuildContext context) {
final s = S.of(context);
final theme = Theme.of(context);
final mq = MediaQuery.of(context);
return BlocProvider<CharactersPerRegionBloc>(
create: (context) => Injection.charactersPerRegionBloc..add(CharactersPerRegionEvent.init(type: regionType)),
child: AlertDialog(
title: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
s.translateRegionType(regionType),
overflow: TextOverflow.ellipsis,
),
Text(
s.characters,
overflow: TextOverflow.ellipsis,
style: theme.textTheme.caption,
),
],
),
actions: [
ElevatedButton(
onPressed: () => Navigator.pop(context),
child: Text(s.ok),
)
],
content: BlocBuilder<CharactersPerRegionBloc, CharactersPerRegionState>(
builder: (context, state) => state.maybeMap(
loaded: (state) => state.items.isEmpty
? const NothingFoundColumn()
: SizedBox(
height: mq.getHeightForDialogs(state.items.length + 1),
width: mq.getWidthForDialogs(),
child: ListView.builder(
itemCount: state.items.length,
itemBuilder: (context, index) => DialogListItemRow.fromItem(itemType: ItemType.character, item: state.items[index]),
),
),
orElse: () => const Loading(useScaffold: false),
),
),
),
);
}
}
65 changes: 65 additions & 0 deletions lib/presentation/shared/dialogs/dialog_list_item_row.dart
@@ -0,0 +1,65 @@
import 'package:flutter/material.dart';
import 'package:shiori/domain/enums/enums.dart';
import 'package:shiori/domain/models/models.dart';
import 'package:shiori/presentation/shared/images/circle_character.dart';
import 'package:shiori/presentation/shared/images/circle_weapon.dart';
import 'package:shiori/presentation/shared/styles.dart';

typedef RowEndWidget = Widget Function(String);

class DialogListItemRow extends StatelessWidget {
final ItemType itemType;
final String itemKey;
final String image;
final String name;
final RowEndWidget? getRowEndWidget;

const DialogListItemRow({
Key? key,
required this.itemType,
required this.itemKey,
required this.image,
required this.name,
this.getRowEndWidget,
}) : assert(itemType == ItemType.character || itemType == ItemType.weapon),
super(key: key);

DialogListItemRow.fromItem({
Key? key,
required ItemType itemType,
required ItemCommonWithName item,
RowEndWidget? getRightWidget,
}) : this(key: key, itemType: itemType, itemKey: item.key, image: item.image, name: item.name, getRowEndWidget: getRightWidget);

@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Row(
children: [
if (itemType == ItemType.character)
CircleCharacter(itemKey: itemKey, image: image, radius: 40)
else
CircleWeapon(itemKey: itemKey, image: image, radius: 40),
Expanded(
child: Padding(
padding: Styles.edgeInsetHorizontal16,
child: getRowEndWidget != null
? getRowEndWidget!.call(itemKey)
: Text(
name,
overflow: TextOverflow.ellipsis,
maxLines: 2,
style: theme.textTheme.subtitle1,
),
),
),
],
),
const Divider(),
],
);
}
}

0 comments on commit 0c47420

Please sign in to comment.