Skip to content

Commit

Permalink
feat: Categorize recent items #149
Browse files Browse the repository at this point in the history
Add categories for recent items, make recent category titles smaller and italic to visually separate them from original categories. Recent items are collapsed by default to reduce the clutter for long lists.
  • Loading branch information
rmax1024 committed Feb 22, 2024
1 parent 89f87d1 commit 48365bb
Show file tree
Hide file tree
Showing 3 changed files with 150 additions and 2 deletions.
7 changes: 6 additions & 1 deletion kitchenowl/lib/pages/household_page/shoppinglist.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import 'package:kitchenowl/models/item.dart';
import 'package:kitchenowl/kitchenowl.dart';
import 'package:kitchenowl/widgets/choice_scroll.dart';
import 'package:kitchenowl/widgets/home_page/sliver_category_item_grid_list.dart';
import 'package:kitchenowl/widgets/home_page/sliver_category_recent_item_grid_list.dart';

class ShoppinglistPage extends StatefulWidget {
const ShoppinglistPage({super.key});
Expand Down Expand Up @@ -246,7 +247,7 @@ class _ShoppinglistPageState extends State<ShoppinglistPage> {
if (body is! List) body,
if ((state.recentItems.isNotEmpty ||
state is LoadingShoppinglistCubitState))
SliverCategoryItemGridList(
SliverCategoryRecentItemGridList(
name:
'${AppLocalizations.of(context)!.itemsRecent}:',
items: state.recentItems,
Expand All @@ -255,6 +256,10 @@ class _ShoppinglistPageState extends State<ShoppinglistPage> {
shoppingList: state.selectedShoppinglist,
onRefresh: cubit.refresh,
isLoading: state is LoadingShoppinglistCubitState,
splitByCategories: !(state.sorting !=
ShoppinglistSorting.category ||
state is LoadingShoppinglistCubitState &&
state.listItems.isEmpty),
),
],
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class SliverCategoryItemGridList<T extends Item> extends StatefulWidget {
final bool isLoading;
final bool? allRaised;
final Widget Function(T)? extraOption;
final bool isSubTitle;

const SliverCategoryItemGridList({
super.key,
Expand All @@ -35,6 +36,7 @@ class SliverCategoryItemGridList<T extends Item> extends StatefulWidget {
this.isLoading = false,
this.allRaised,
this.extraOption,
this.isSubTitle = false
});

@override
Expand All @@ -48,6 +50,9 @@ class _SliverCategoryItemGridListState<T extends Item>

@override
Widget build(BuildContext context) {
TextStyle? titleTextStyle = Theme.of(context).textTheme.titleLarge;
if (widget.isSubTitle)
titleTextStyle = titleTextStyle?.apply(fontStyle: FontStyle.italic, fontWeightDelta: -1);
return SliverMainAxisGroup(
slivers: [
SliverToBoxAdapter(
Expand All @@ -64,7 +69,7 @@ class _SliverCategoryItemGridListState<T extends Item>
Expanded(
child: Text(
widget.name,
style: Theme.of(context).textTheme.titleLarge,
style: titleTextStyle,
),
),
IconButton(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import 'package:flutter/material.dart';
import 'package:kitchenowl/kitchenowl.dart';
import 'package:kitchenowl/models/category.dart';
import 'package:kitchenowl/models/household.dart';
import 'package:kitchenowl/models/item.dart';
import 'package:kitchenowl/models/shoppinglist.dart';
import 'package:sliver_tools/sliver_tools.dart';
import 'package:kitchenowl/widgets/home_page/sliver_category_item_grid_list.dart';

class SliverCategoryRecentItemGridList<T extends Item> extends StatefulWidget {
final String name;
final Duration animationDuration;

// SliverItemGridList
final void Function()? onRefresh;
final Nullable<void Function(T)>? onPressed;
final Nullable<void Function(T)>? onLongPressed;
final List<T> items;
final List<Category> categories; // forwarded to item page on long press
final ShoppingList? shoppingList; // forwarded to item page on long press
final Household?
household; // forwarded to item page on long press for offline functionality
final bool Function(T)? selected;
final bool isLoading;
final bool? allRaised;
final bool splitByCategories;

const SliverCategoryRecentItemGridList({
super.key,
required this.name,
this.animationDuration = const Duration(milliseconds: 150),
this.onRefresh,
this.onPressed,
this.onLongPressed,
this.items = const [],
this.household,
this.categories = const [],
this.shoppingList,
this.selected,
this.isLoading = false,
this.allRaised,
this.splitByCategories = false,
});

@override
State<SliverCategoryRecentItemGridList<T>> createState() =>
_SliverCategoryRecentItemGridListState<T>();
}

class _SliverCategoryRecentItemGridListState<T extends Item>
extends State<SliverCategoryRecentItemGridList<T>> {
bool isExpanded = false;

@override
Widget build(BuildContext context) {
List<Widget> list = [];

if (widget.splitByCategories) {
for (int i = 0; i < widget.categories.length + 1; i++) {
Category? category = i < widget.categories.length
? widget.categories[i]
: null;
final List<T> items = widget.items
.where((e) => e.category == category)
.toList();
if (items.isEmpty) continue;

list.add(SliverCategoryItemGridList(
name: category?.name ??
AppLocalizations.of(context)!.uncategorized,
items: items,
categories: widget.categories,
shoppingList: widget.shoppingList,
selected: (item) => false,
isLoading: widget.isLoading,
onRefresh: widget.onRefresh,
onPressed: widget.onPressed,
isSubTitle: true
));
}
}
else
list.add(SliverItemGridList<T>(
onRefresh: widget.onRefresh,
onPressed: widget.onPressed,
onLongPressed: widget.onLongPressed,
items: widget.items,
categories: widget.categories,
shoppingList: widget.shoppingList,
selected: widget.selected,
isLoading: widget.isLoading,
allRaised: widget.allRaised,
));

return SliverMainAxisGroup(
slivers: [
SliverToBoxAdapter(
child: AnimatedPadding(
padding: EdgeInsets.fromLTRB(16, 0, 16, isExpanded ? 8 : 4),
duration: widget.animationDuration,
child: InkWell(
onTap: () => setState(() {
isExpanded = !isExpanded;
}),
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Expanded(
child: Text(
widget.name,
style: Theme.of(context).textTheme.titleLarge,
),
),
IconButton(
onPressed: () => setState(() {
isExpanded = !isExpanded;
}),
icon: AnimatedRotation(
duration: widget.animationDuration,
turns: isExpanded ? 0 : .25,
child: const Icon(Icons.expand_more_rounded),
),
),
],
),
),
),
),
SliverAnimatedSwitcher(
duration: widget.animationDuration,
child: !isExpanded
? const SliverToBoxAdapter(child: SizedBox())
: MultiSliver(children: list),
),
],
);
}
}

0 comments on commit 48365bb

Please sign in to comment.