Skip to content

Commit

Permalink
feat: Add expense image (#76)
Browse files Browse the repository at this point in the history
  • Loading branch information
TomBursch authored Oct 25, 2022
1 parent d0a041e commit 7659056
Show file tree
Hide file tree
Showing 9 changed files with 214 additions and 127 deletions.
51 changes: 40 additions & 11 deletions lib/cubits/expense_add_update_cubit.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'dart:io';

import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:kitchenowl/kitchenowl.dart';
Expand All @@ -21,23 +23,34 @@ class AddUpdateExpenseCubit extends Cubit<AddUpdateExpenseState> {
}

Future<void> saveExpense() async {
if (state.isValid()) {
final amount = state.amount * (state.isIncome ? -1 : 1);
final _state = state;
if (_state.isValid()) {
final amount = _state.amount * (_state.isIncome ? -1 : 1);
String? image;
if (_state.image != null) {
if (_state.image!.path.isEmpty) {
image = '';
} else if (await _state.image!.exists()) {
image = await ApiService.getInstance().uploadFile(_state.image!);
}
}
if (expense.id == null) {
await ApiService.getInstance().addExpense(Expense(
amount: amount,
name: state.name,
paidById: state.paidBy,
paidFor: state.paidFor,
category: state.category,
name: _state.name,
image: image ?? expense.image,
paidById: _state.paidBy,
paidFor: _state.paidFor,
category: _state.category,
));
} else {
await ApiService.getInstance().updateExpense(expense.copyWith(
name: state.name,
amount: amount,
paidById: state.paidBy,
paidFor: state.paidFor,
category: Nullable(state.category),
image: image,
paidById: _state.paidBy,
paidFor: _state.paidFor,
category: Nullable(_state.category),
));
}
}
Expand All @@ -63,6 +76,10 @@ class AddUpdateExpenseCubit extends Cubit<AddUpdateExpenseState> {
emit(state.copyWith(isIncome: isIncome));
}

void setImage(File image) {
emit(state.copyWith(image: image));
}

void setPaidBy(User user) {
emit(state.copyWith(paidBy: user.id));
}
Expand Down Expand Up @@ -128,6 +145,7 @@ class AddUpdateExpenseState extends Equatable {
final String name;
final double amount;
final bool isIncome;
final File? image;
final int paidBy;
final List<PaidForModel> paidFor;
final String? category;
Expand All @@ -137,6 +155,7 @@ class AddUpdateExpenseState extends Equatable {
this.name = "",
required this.amount,
this.isIncome = false,
this.image,
required this.paidBy,
this.category,
this.paidFor = const [],
Expand All @@ -148,6 +167,7 @@ class AddUpdateExpenseState extends Equatable {
double? amount,
int? paidBy,
bool? isIncome,
File? image,
List<PaidForModel>? paidFor,
String? category,
List<String>? categories,
Expand All @@ -156,6 +176,7 @@ class AddUpdateExpenseState extends Equatable {
name: name ?? this.name,
amount: amount ?? this.amount,
isIncome: isIncome ?? this.isIncome,
image: image ?? this.image,
category: category ?? this.category,
paidBy: paidBy ?? this.paidBy,
paidFor: paidFor ?? this.paidFor,
Expand All @@ -179,6 +200,14 @@ class AddUpdateExpenseState extends Equatable {
bool isValid() => name.isNotEmpty && amount != 0 && paidFor.isNotEmpty;

@override
List<Object?> get props =>
[name, amount, isIncome, paidBy, category, categories] + paidFor;
List<Object?> get props => [
name,
amount,
isIncome,
image,
paidBy,
category,
categories,
paidFor,
];
}
8 changes: 7 additions & 1 deletion lib/models/expense.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ class Expense extends Model {
final int? id;
final String name;
final double amount;
final String image;
final DateTime? createdAt;
final String? category;
final int paidById;
Expand All @@ -15,6 +16,7 @@ class Expense extends Model {
this.id,
this.name = '',
this.amount = 0,
this.image = "",
required this.paidById,
this.paidFor = const [],
this.createdAt,
Expand All @@ -31,6 +33,7 @@ class Expense extends Model {
id: map['id'],
name: map['name'],
amount: map['amount'],
image: map['photo'] ?? "",
category: map['category'],
createdAt:
DateTime.fromMillisecondsSinceEpoch(map['created_at'], isUtc: true)
Expand All @@ -43,6 +46,7 @@ class Expense extends Model {
Expense copyWith({
String? name,
double? amount,
String? image,
Nullable<String>? category,
int? paidById,
List<PaidForModel>? paidFor,
Expand All @@ -51,19 +55,21 @@ class Expense extends Model {
id: id,
name: name ?? this.name,
amount: amount ?? this.amount,
image: image ?? this.image,
category: (category ?? Nullable(this.category)).value,
paidById: paidById ?? this.paidById,
paidFor: paidFor ?? this.paidFor,
);

@override
List<Object?> get props =>
[id, name, amount, category, createdAt, paidById] + paidFor;
[id, name, amount, image, category, createdAt, paidById, paidFor];

@override
Map<String, dynamic> toJson() => {
"name": name,
"amount": amount,
"photo": image,
'category': category,
"paid_by": {"id": paidById},
"paid_for": paidFor.map((e) => e.toJson()).toList(),
Expand Down
10 changes: 10 additions & 0 deletions lib/pages/expense_add_update_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,16 @@ class _AddUpdateRecipePageState extends State<AddUpdateExpensePage> {
SliverList(
delegate: SliverChildListDelegate(
[
BlocBuilder<AddUpdateExpenseCubit, AddUpdateExpenseState>(
bloc: cubit,
buildWhen: (previous, current) =>
previous.image != current.image,
builder: (context, state) => ImageSelector(
image: state.image,
originalImage: cubit.expense.image,
setImage: cubit.setImage,
),
),
Padding(
padding: const EdgeInsets.all(16),
child: TextField(
Expand Down
7 changes: 6 additions & 1 deletion lib/pages/expense_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,12 @@ class _ExpensePageState extends State<ExpensePage> {
child: CustomScrollView(
slivers: [
SliverAppBar(
title: Text(state.expense.name),
flexibleSpace: FlexibleImageSpaceBar(
title: state.expense.name,
imageUrl: state.expense.image,
),
expandedHeight: state.expense.image.isNotEmpty ? 160 : null,
pinned: true,
leading: BackButton(
onPressed: () =>
Navigator.of(context).pop(cubit.state.updateState),
Expand Down
52 changes: 4 additions & 48 deletions lib/pages/recipe_add_update_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -105,54 +105,10 @@ class _AddUpdateRecipePageState extends State<AddUpdateRecipePage> {
bloc: cubit,
buildWhen: (previous, current) =>
previous.image != current.image,
builder: (context, state) => Container(
margin: const EdgeInsets.all(16),
constraints: const BoxConstraints.expand(height: 80),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(14),
border: Border.all(
color: Theme.of(context).colorScheme.secondary,
width: 2,
),
image: (state.image != null &&
state.image!.path.isNotEmpty ||
state.image == null &&
cubit.recipe.image.isNotEmpty)
? DecorationImage(
fit: BoxFit.cover,
opacity: .5,
image: state.image != null
? FileImage(state.image!)
: getImageProvider(
context,
cubit.recipe.image,
),
)
: null,
),
child: IconButton(
icon: (state.image != null &&
state.image!.path.isNotEmpty ||
state.image == null &&
cubit.recipe.image.isNotEmpty)
? const Icon(Icons.edit)
: const Icon(Icons.add_photo_alternate_rounded),
color: Theme.of(context).colorScheme.secondary,
onPressed: () async {
File? file = await selectFile(
context: context,
title:
AppLocalizations.of(context)!.recipeImageSelect,
deleteOption: (state.image != null &&
state.image!.path.isNotEmpty ||
state.image == null &&
cubit.recipe.image.isNotEmpty),
);
if (file != null) {
cubit.setImage(file);
}
},
),
builder: (context, state) => ImageSelector(
image: state.image,
originalImage: cubit.recipe.image,
setImage: cubit.setImage,
),
),
Padding(
Expand Down
69 changes: 3 additions & 66 deletions lib/pages/recipe_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -63,72 +63,9 @@ class _RecipePageState extends State<RecipePage> {
primary: true,
slivers: [
SliverAppBar(
flexibleSpace: FlexibleSpaceBar(
titlePadding: const EdgeInsetsDirectional.only(
start: 60,
bottom: 16,
end: 36,
),
title: LayoutBuilder(builder: (context, constraints) {
final isCollapsed = constraints.biggest.height <=
MediaQuery.of(context).padding.top +
kToolbarHeight -
16 +
32;

return Text(
state.recipe.name,
maxLines: isCollapsed ? 1 : 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: Theme.of(context).colorScheme.onBackground,
),
);
}),
background: state.recipe.image.isNotEmpty
? GestureDetector(
onTap: () =>
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => PhotoViewPage(
title: state.recipe.name,
imageProvider: getImageProvider(
context,
state.recipe.image,
),
// heroTag: state.recipe.image, # TODO cannot use Hero inside OpenContainer
),
)),
child:
// Hero(
// tag: state.recipe.image,
// flightShuttleBuilder: (
// BuildContext flightContext,
// Animation<double> animation,
// HeroFlightDirection flightDirection,
// BuildContext fromHeroContext,
// BuildContext toHeroContext,
// ) {
// final Hero hero = flightDirection ==
// HeroFlightDirection.push
// ? fromHeroContext.widget as Hero
// : toHeroContext.widget as Hero;

// return hero.child;
// },
Image(
image: getImageProvider(
context,
state.recipe.image,
),
color: Theme.of(context)
.backgroundColor
.withOpacity(.25),
colorBlendMode: BlendMode.srcATop,
fit: BoxFit.cover,
),
// ),
)
: null,
flexibleSpace: FlexibleImageSpaceBar(
title: state.recipe.name,
imageUrl: state.recipe.image,
),
leading: BackButton(
onPressed: () =>
Expand Down
2 changes: 2 additions & 0 deletions lib/widgets/_export.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,5 @@ export 'show_snack_bar.dart';
export 'shimmer_card.dart';
export 'shimmer_shopping_item.dart';
export 'kitchenowl_switch.dart';
export 'image_selector.dart';
export 'flexible_image_space_bar.dart';
Loading

0 comments on commit 7659056

Please sign in to comment.