Skip to content

Commit

Permalink
feat: tablet mode
Browse files Browse the repository at this point in the history
  • Loading branch information
TomBursch committed Oct 5, 2021
1 parent c194284 commit 395ff6f
Show file tree
Hide file tree
Showing 6 changed files with 198 additions and 103 deletions.
18 changes: 18 additions & 0 deletions lib/cubits/planner_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@ class PlannerCubit extends Cubit<PlannerCubitState> {
[];
emit(PlannerCubitState(planned, recent, suggested));
}

Future<void> refreshSuggestions() async {
final suggested = await TransactionHandler.getInstance()
.runTransaction(TransactionPlannerRefreshSuggestedRecipes()) ??
[];
emit(state.copyWith(suggestedRecipes: suggested));
}
}

class PlannerCubitState extends Equatable {
Expand All @@ -48,4 +55,15 @@ class PlannerCubitState extends Equatable {
@override
List<Object> get props =>
plannedRecipes.cast<Object>() + recentRecipes + suggestedRecipes;

PlannerCubitState copyWith({
List<Recipe> plannedRecipes,
List<Recipe> recentRecipes,
List<Recipe> suggestedRecipes,
}) =>
PlannerCubitState(
plannedRecipes ?? this.plannedRecipes,
recentRecipes ?? this.recentRecipes,
suggestedRecipes ?? this.suggestedRecipes,
);
}
105 changes: 76 additions & 29 deletions lib/pages/home_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import 'package:kitchenowl/pages/expense_add_update_page.dart';
import 'package:kitchenowl/pages/recipe_add_update_page.dart';
import 'package:kitchenowl/pages/home_page/home_page.dart';
import 'package:kitchenowl/kitchenowl.dart';
import 'package:responsive_builder/responsive_builder.dart';

class HomePage extends StatefulWidget {
const HomePage({Key key}) : super(key: key);
Expand Down Expand Up @@ -215,38 +216,84 @@ class _HomePageState extends State<HomePage> {
),
];

return Scaffold(
body: PageTransitionSwitcher(
transitionBuilder: (
Widget child,
Animation<double> animation,
Animation<double> secondaryAnimation,
) {
return FadeThroughTransition(
animation: animation,
secondaryAnimation: secondaryAnimation,
child: child,
);
},
child: Align(
alignment: Alignment.topCenter,
child: ConstrainedBox(
constraints: const BoxConstraints.expand(width: 1600),
child: _homePageMenuItems[_selectedIndex].page),
),
Widget body = PageTransitionSwitcher(
transitionBuilder: (
Widget child,
Animation<double> animation,
Animation<double> secondaryAnimation,
) {
return FadeThroughTransition(
animation: animation,
secondaryAnimation: secondaryAnimation,
child: child,
);
},
child: Align(
alignment: Alignment.topCenter,
child: ConstrainedBox(
constraints: const BoxConstraints.expand(width: 1600),
child: _homePageMenuItems[_selectedIndex].page),
),
);

final bool useBottomNavigationBar = getValueForScreenType<bool>(
context: context,
mobile: true,
tablet: false,
desktop: false,
);

if (!useBottomNavigationBar) {
body = Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
SizedBox(
width: 200,
child: ListView(
children: _homePageMenuItems
.asMap()
.entries
.map(
(e) => ListTile(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.horizontal(
right: Radius.circular(5)),
),
tileColor: _selectedIndex == e.key
? Theme.of(context).colorScheme.primary
: null,
title:
Text(e.value.bottomNavigationBarItem.label),
leading: e.value.bottomNavigationBarItem.icon,
onTap: () =>
_onItemTapped(e.key, _homePageMenuItems),
),
)
.toList(),
),
),
const VerticalDivider(),
Expanded(child: body),
],
);
}

return Scaffold(
body: body,
floatingActionButton:
_homePageMenuItems[_selectedIndex].floatingActionButton,
bottomNavigationBar: BottomNavigationBar(
showUnselectedLabels: false,
showSelectedLabels: true,
type: BottomNavigationBarType.fixed,
items: _homePageMenuItems
.map((e) => e.bottomNavigationBarItem)
.toList(),
currentIndex: _selectedIndex,
onTap: (i) => _onItemTapped(i, _homePageMenuItems),
),
bottomNavigationBar: useBottomNavigationBar
? BottomNavigationBar(
showUnselectedLabels: false,
showSelectedLabels: true,
type: BottomNavigationBarType.fixed,
items: _homePageMenuItems
.map((e) => e.bottomNavigationBarItem)
.toList(),
currentIndex: _selectedIndex,
onTap: (i) => _onItemTapped(i, _homePageMenuItems),
)
: null,
);
},
);
Expand Down
135 changes: 64 additions & 71 deletions lib/pages/home_page/expense_list.dart
Original file line number Diff line number Diff line change
Expand Up @@ -54,77 +54,7 @@ class _ExpensePageState extends State<ExpenseListPage> {
SliverToBoxAdapter(
child: SizedBox(
height: (state.users.length * 60 + 30).toDouble(),
child: charts.BarChart(
[
charts.Series<User, String>(
id: 'Balance',
data: state.users,
colorFn: (user, _) => charts.Color(
r: Theme.of(context).colorScheme.secondary.red,
g: Theme.of(context).colorScheme.secondary.green,
b: Theme.of(context).colorScheme.secondary.blue,
),
domainFn: (user, _) => user.username,
measureFn: (user, _) => user.balance,
labelAccessorFn: (user, _) =>
" ${user.name}: ${NumberFormat.simpleCurrency().format(user.balance)}",
),
charts.Series<User, String>(
id: 'zero',
domainFn: (user, _) => user.username,
measureFn: (user, _) => 0,
data: state.users,
colorFn: (user, _) => charts.Color(
r: Theme.of(context).disabledColor.red,
g: Theme.of(context).disabledColor.green,
b: Theme.of(context).disabledColor.blue,
),
strokeWidthPxFn: (user, _) => 5,
)..setAttribute(charts.rendererIdKey, 'zero'),
],
vertical: false,
barRendererDecorator: charts.BarLabelDecorator<String>(
outsideLabelStyleSpec: charts.TextStyleSpec(
color: charts.Color(
r: Theme.of(context)
.textTheme
.bodyText2
.color
.red,
g: Theme.of(context)
.textTheme
.bodyText2
.color
.green,
b: Theme.of(context)
.textTheme
.bodyText2
.color
.blue,
),
),
),
customSeriesRenderers: [
charts.BarTargetLineRendererConfig<String>(
customRendererId: 'zero',
)
],
defaultInteractions: false,
primaryMeasureAxis: charts.NumericAxisSpec(
showAxisLine: false,
renderSpec: const charts.NoneRenderSpec(),
tickProviderSpec:
charts.StaticNumericTickProviderSpec([
charts.TickSpec(-state.users.fold<double>(0.0,
(p, e) => e.balance > p ? e.balance : p)),
const charts.TickSpec<double>(0.0),
charts.TickSpec(state.users.fold<double>(0.0,
(p, e) => e.balance > p ? e.balance : p)),
])),
domainAxis: const charts.OrdinalAxisSpec(
renderSpec: charts.NoneRenderSpec(),
),
),
child: _getBarChart(context, state),
),
),
if (state.expenses.isEmpty && !App.isOffline(context))
Expand Down Expand Up @@ -179,4 +109,67 @@ class _ExpensePageState extends State<ExpenseListPage> {
),
);
}

Widget _getBarChart(BuildContext context, ExpenseListCubitState state) {
double maxBalance = state.users
.fold<double>(0.0, (p, e) => e.balance.abs() > p ? e.balance.abs() : p);
maxBalance = maxBalance > 0 ? maxBalance : 1;

return charts.BarChart(
[
charts.Series<User, String>(
id: 'Balance',
data: state.users,
colorFn: (user, _) => charts.Color(
r: Theme.of(context).colorScheme.secondary.red,
g: Theme.of(context).colorScheme.secondary.green,
b: Theme.of(context).colorScheme.secondary.blue,
),
domainFn: (user, _) => user.username,
measureFn: (user, _) => user.balance,
labelAccessorFn: (user, _) =>
" ${user.name}: ${NumberFormat.simpleCurrency().format(user.balance)}",
),
charts.Series<User, String>(
id: 'zero',
domainFn: (user, _) => user.username,
measureFn: (user, _) => 0,
data: state.users,
colorFn: (user, _) => charts.Color(
r: Theme.of(context).disabledColor.red,
g: Theme.of(context).disabledColor.green,
b: Theme.of(context).disabledColor.blue,
),
strokeWidthPxFn: (user, _) => 5,
)..setAttribute(charts.rendererIdKey, 'zero'),
],
vertical: false,
barRendererDecorator: charts.BarLabelDecorator<String>(
outsideLabelStyleSpec: charts.TextStyleSpec(
color: charts.Color(
r: Theme.of(context).textTheme.bodyText2.color.red,
g: Theme.of(context).textTheme.bodyText2.color.green,
b: Theme.of(context).textTheme.bodyText2.color.blue,
),
),
),
customSeriesRenderers: [
charts.BarTargetLineRendererConfig<String>(
customRendererId: 'zero',
)
],
defaultInteractions: false,
primaryMeasureAxis: charts.NumericAxisSpec(
showAxisLine: false,
renderSpec: const charts.NoneRenderSpec(),
tickProviderSpec: charts.StaticNumericTickProviderSpec([
charts.TickSpec(-maxBalance),
const charts.TickSpec<double>(0.0),
charts.TickSpec(maxBalance),
])),
domainAxis: const charts.OrdinalAxisSpec(
renderSpec: charts.NoneRenderSpec(),
),
);
}
}
18 changes: 15 additions & 3 deletions lib/pages/home_page/planner.dart
Original file line number Diff line number Diff line change
Expand Up @@ -153,9 +153,21 @@ class _PlannerPageState extends State<PlannerPage> {
SliverPadding(
padding: const EdgeInsets.all(16),
sliver: SliverToBoxAdapter(
child: Text(
AppLocalizations.of(context).recipesSuggested + ':',
style: Theme.of(context).textTheme.headline6,
child: Row(
children: [
Expanded(
child: Text(
AppLocalizations.of(context).recipesSuggested +
':',
style: Theme.of(context).textTheme.headline6,
),
),
InkWell(
borderRadius: BorderRadius.circular(50),
child: const Icon(Icons.refresh),
onTap: cubit.refreshSuggestions,
),
],
),
),
),
Expand Down
8 changes: 8 additions & 0 deletions lib/services/api/planner.dart
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,12 @@ extension PlannerApi on ApiService {
final body = List.from(jsonDecode(res.body));
return body.map((e) => Recipe.fromJson(e)).toList();
}

Future<List<Recipe>> refreshSuggestedRecipes() async {
final res = await get('/planner/refresh-suggested-recipes');
if (res.statusCode != 200) return [];

final body = List.from(jsonDecode(res.body));
return body.map((e) => Recipe.fromJson(e)).toList();
}
}
17 changes: 17 additions & 0 deletions lib/services/transactions/planner.dart
Original file line number Diff line number Diff line change
Expand Up @@ -121,3 +121,20 @@ class TransactionPlannerRemoveRecipe extends Transaction<bool> {
return ApiService.getInstance().removePlannedRecipe(recipe);
}
}

class TransactionPlannerRefreshSuggestedRecipes
extends Transaction<List<Recipe>> {
TransactionPlannerRefreshSuggestedRecipes({DateTime timestamp})
: super.internal(timestamp ?? DateTime.now(),
"TransactionPlannerRefreshSuggestedRecipes");

@override
Future<List<Recipe>> runLocal() async {
return [];
}

@override
Future<List<Recipe>> runOnline() async {
return await ApiService.getInstance().refreshSuggestedRecipes();
}
}

0 comments on commit 395ff6f

Please sign in to comment.