From b6b11d9b50ec84a50541d35aa9dd7833e0533ea3 Mon Sep 17 00:00:00 2001 From: Linard Schwendener Date: Sun, 2 Aug 2020 22:53:16 +0200 Subject: [PATCH 01/17] Use integer if possible! --- lib/src/models/recipe_short.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/src/models/recipe_short.dart b/lib/src/models/recipe_short.dart index bc8d3753..01461fa1 100644 --- a/lib/src/models/recipe_short.dart +++ b/lib/src/models/recipe_short.dart @@ -12,7 +12,9 @@ class RecipeShort extends Equatable { String get imageUrl => _imageUrl; RecipeShort.fromJson(Map json) - : _recipeId = int.parse(json["recipe_id"]), + : _recipeId = json["recipe_id"] is int + ? json["recipe_id"] + : int.parse(json["recipe_id"]), _name = json["name"], _imageUrl = json["imageUrl"]; From 54c802c6676d6c310ecc067dd435f209f6036494 Mon Sep 17 00:00:00 2001 From: Linard Schwendener Date: Sun, 2 Aug 2020 23:21:15 +0200 Subject: [PATCH 02/17] Debug + Check for empty String --- lib/src/blocs/recipe/recipe_bloc.dart | 8 ++++---- lib/src/blocs/recipe/recipe_state.dart | 11 +++++++++-- lib/src/models/recipe.dart | 11 ++++++----- lib/src/screens/recipe_screen.dart | 4 ++++ 4 files changed, 23 insertions(+), 11 deletions(-) diff --git a/lib/src/blocs/recipe/recipe_bloc.dart b/lib/src/blocs/recipe/recipe_bloc.dart index cbae0388..8ab51b2b 100644 --- a/lib/src/blocs/recipe/recipe_bloc.dart +++ b/lib/src/blocs/recipe/recipe_bloc.dart @@ -15,14 +15,14 @@ class RecipeBloc extends Bloc { } } - - Stream _mapRecipeLoadedToState(RecipeLoaded recipeLoaded) async* { + Stream _mapRecipeLoadedToState( + RecipeLoaded recipeLoaded) async* { try { yield RecipeLoadInProgress(); final recipe = await dataRepository.fetchRecipe(recipeLoaded.recipeId); yield RecipeLoadSuccess(recipe: recipe); } catch (_) { - yield RecipeLoadFailure(); + yield RecipeLoadFailure(_.toString()); } } -} \ No newline at end of file +} diff --git a/lib/src/blocs/recipe/recipe_state.dart b/lib/src/blocs/recipe/recipe_state.dart index 6f11f028..c948d5c8 100644 --- a/lib/src/blocs/recipe/recipe_state.dart +++ b/lib/src/blocs/recipe/recipe_state.dart @@ -20,6 +20,13 @@ class RecipeLoadSuccess extends RecipeState { List get props => [recipe]; } -class RecipeLoadFailure extends RecipeState {} +class RecipeLoadFailure extends RecipeState { + final String errorMsg; -class RecipeLoadInProgress extends RecipeState {} \ No newline at end of file + const RecipeLoadFailure(this.errorMsg); + + @override + List get props => [errorMsg]; +} + +class RecipeLoadInProgress extends RecipeState {} diff --git a/lib/src/models/recipe.dart b/lib/src/models/recipe.dart index 6a3ef037..d2c5b3f3 100644 --- a/lib/src/models/recipe.dart +++ b/lib/src/models/recipe.dart @@ -42,15 +42,16 @@ class Recipe extends Equatable { List recipeInstructions = data["recipeInstructions"].cast().toList(); int recipeYield = data["recipeYield"]; - Duration prepTime = data.containsKey("prepTime") + Duration prepTime = data.containsKey("prepTime") && data["prepTime"] != "" ? IsoTimeFormat.toDuration(data["prepTime"]) : null; - Duration cookTime = data.containsKey("cookTime") + Duration cookTime = data.containsKey("cookTime") && data["cookTime"] != "" ? IsoTimeFormat.toDuration(data["cookTime"]) : null; - Duration totalTime = data.containsKey("totalTime") - ? IsoTimeFormat.toDuration(data["totalTime"]) - : null; + Duration totalTime = + data.containsKey("totalTime") && data["totalTime"] != "" + ? IsoTimeFormat.toDuration(data["totalTime"]) + : null; return Recipe._( id, diff --git a/lib/src/screens/recipe_screen.dart b/lib/src/screens/recipe_screen.dart index 17edc3c2..dda93704 100644 --- a/lib/src/screens/recipe_screen.dart +++ b/lib/src/screens/recipe_screen.dart @@ -41,6 +41,10 @@ class RecipeScreenState extends State { return Center( child: CircularProgressIndicator(), ); + } else if (state is RecipeLoadFailure) { + return Center( + child: Text(state.errorMsg), + ); } else { return Center( child: Text("FAILED"), From 5a4339fe438c9156eb26756b777b4af17c458032 Mon Sep 17 00:00:00 2001 From: Linard Schwendener Date: Sat, 8 Aug 2020 16:13:40 +0200 Subject: [PATCH 03/17] Updated Version number --- android/app/build.gradle | 2 +- pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 98a29db6..b3fc4710 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -41,7 +41,7 @@ android { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "com.nextcloud_cookbook_flutter" minSdkVersion 18 - targetSdkVersion 28 + targetSdkVersion 29 versionCode flutterVersionCode.toInteger() versionName flutterVersionName testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" diff --git a/pubspec.yaml b/pubspec.yaml index 627605f9..ac24a756 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -11,7 +11,7 @@ description: A new Flutter application. # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 0.3.0+6 +version: 0.3.1+7 environment: sdk: ">=2.1.0 <3.0.0" From 71c95edaf641104e4fb29927b5c71de41ee327ee Mon Sep 17 00:00:00 2001 From: Linard Schwendener Date: Fri, 14 Aug 2020 23:29:11 +0200 Subject: [PATCH 04/17] Added new bloc events and states --- lib/src/blocs/recipe/recipe_bloc.dart | 2 +- lib/src/blocs/recipe/recipe_event.dart | 12 ++++++++- lib/src/blocs/recipe/recipe_state.dart | 35 +++++++++++++++++++------- 3 files changed, 38 insertions(+), 11 deletions(-) diff --git a/lib/src/blocs/recipe/recipe_bloc.dart b/lib/src/blocs/recipe/recipe_bloc.dart index 8ab51b2b..bd499f00 100644 --- a/lib/src/blocs/recipe/recipe_bloc.dart +++ b/lib/src/blocs/recipe/recipe_bloc.dart @@ -20,7 +20,7 @@ class RecipeBloc extends Bloc { try { yield RecipeLoadInProgress(); final recipe = await dataRepository.fetchRecipe(recipeLoaded.recipeId); - yield RecipeLoadSuccess(recipe: recipe); + yield RecipeLoadSuccess(recipe); } catch (_) { yield RecipeLoadFailure(_.toString()); } diff --git a/lib/src/blocs/recipe/recipe_event.dart b/lib/src/blocs/recipe/recipe_event.dart index fc7a87b8..e405fea8 100644 --- a/lib/src/blocs/recipe/recipe_event.dart +++ b/lib/src/blocs/recipe/recipe_event.dart @@ -1,5 +1,6 @@ import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; +import 'package:nextcloud_cookbook_flutter/src/models/recipe.dart'; abstract class RecipeEvent extends Equatable { const RecipeEvent(); @@ -15,4 +16,13 @@ class RecipeLoaded extends RecipeEvent { @override List get props => [recipeId]; -} \ No newline at end of file +} + +class RecipeUpdated extends RecipeEvent { + final Recipe recipe; + + const RecipeUpdated(this.recipe); + + @override + List get props => [recipe]; +} diff --git a/lib/src/blocs/recipe/recipe_state.dart b/lib/src/blocs/recipe/recipe_state.dart index c948d5c8..c2811e4b 100644 --- a/lib/src/blocs/recipe/recipe_state.dart +++ b/lib/src/blocs/recipe/recipe_state.dart @@ -1,5 +1,4 @@ import 'package:equatable/equatable.dart'; -import 'package:flutter/cupertino.dart'; import 'package:nextcloud_cookbook_flutter/src/models/recipe.dart'; abstract class RecipeState extends Equatable { @@ -11,22 +10,40 @@ abstract class RecipeState extends Equatable { class RecipeInitial extends RecipeState {} -class RecipeLoadSuccess extends RecipeState { - final Recipe recipe; +class RecipeFailure extends RecipeState { + final String errorMsg; - RecipeLoadSuccess({@required this.recipe}); + const RecipeFailure(this.errorMsg); @override - List get props => [recipe]; + List get props => [errorMsg]; } -class RecipeLoadFailure extends RecipeState { - final String errorMsg; +class RecipeSuccess extends RecipeState { + final Recipe recipe; - const RecipeLoadFailure(this.errorMsg); + const RecipeSuccess(this.recipe); @override - List get props => [errorMsg]; + List get props => [recipe]; +} + +class RecipeLoadSuccess extends RecipeSuccess { + RecipeLoadSuccess(Recipe recipe) : super(recipe); +} + +class RecipeLoadFailure extends RecipeFailure { + RecipeLoadFailure(String errorMsg) : super(errorMsg); } class RecipeLoadInProgress extends RecipeState {} + +class RecipeUpdateFailure extends RecipeFailure { + RecipeUpdateFailure(String errorMsg) : super(errorMsg); +} + +class RecipeUpdateSuccess extends RecipeSuccess { + RecipeUpdateSuccess(Recipe recipe) : super(recipe); +} + +class RecipeUpdateInProgress extends RecipeState {} From 6b7b2757c534475fc77856842ec21fca56d0abf1 Mon Sep 17 00:00:00 2001 From: Linard Schwendener Date: Fri, 14 Aug 2020 23:32:40 +0200 Subject: [PATCH 05/17] Added MOCK BLOC Handling --- lib/src/blocs/recipe/recipe_bloc.dart | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/lib/src/blocs/recipe/recipe_bloc.dart b/lib/src/blocs/recipe/recipe_bloc.dart index bd499f00..1b455e6d 100644 --- a/lib/src/blocs/recipe/recipe_bloc.dart +++ b/lib/src/blocs/recipe/recipe_bloc.dart @@ -1,3 +1,5 @@ +import 'dart:developer'; + import 'package:bloc/bloc.dart'; import 'package:nextcloud_cookbook_flutter/src/blocs/recipe/recipe.dart'; import 'package:nextcloud_cookbook_flutter/src/services/data_repository.dart'; @@ -12,6 +14,8 @@ class RecipeBloc extends Bloc { Stream mapEventToState(RecipeEvent event) async* { if (event is RecipeLoaded) { yield* _mapRecipeLoadedToState(event); + } else if (event is RecipeUpdated) { + yield* _mapRecipeUpdatedToState(event); } } @@ -25,4 +29,16 @@ class RecipeBloc extends Bloc { yield RecipeLoadFailure(_.toString()); } } + + Stream _mapRecipeUpdatedToState( + RecipeUpdated recipeUpdated) async* { + try { + yield RecipeUpdateInProgress(); + // TODO: add actual sending of data + log(recipeUpdated.recipe.toString()); + yield RecipeUpdateSuccess(null); + } catch (_) { + yield RecipeUpdateFailure(_.toString()); + } + } } From 7f659b4f9e8f963c19eb496cdbe54e9ed093e091 Mon Sep 17 00:00:00 2001 From: Linard Schwendener Date: Sat, 15 Aug 2020 00:15:45 +0200 Subject: [PATCH 06/17] Added Recipe Edit Screen & Form --- lib/src/screens/form/recipe_form.dart | 56 +++++++++++++++++++++++++ lib/src/screens/recipe_edit_screen.dart | 19 +++++++++ 2 files changed, 75 insertions(+) create mode 100644 lib/src/screens/form/recipe_form.dart create mode 100644 lib/src/screens/recipe_edit_screen.dart diff --git a/lib/src/screens/form/recipe_form.dart b/lib/src/screens/form/recipe_form.dart new file mode 100644 index 00000000..421069a4 --- /dev/null +++ b/lib/src/screens/form/recipe_form.dart @@ -0,0 +1,56 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:nextcloud_cookbook_flutter/src/blocs/recipe/recipe.dart'; +import 'package:nextcloud_cookbook_flutter/src/models/recipe.dart'; + +class RecipeForm extends StatefulWidget { + final Recipe recipe; + + const RecipeForm(this.recipe); + + @override + _RecipeFormState createState() => _RecipeFormState(); +} + +class _RecipeFormState extends State { + final _formKey = GlobalKey(); + Recipe recipe; + MutableRecipe _mutableRecipe = MutableRecipe(); + + @override + void initState() { + recipe = widget.recipe; + + _mutableRecipe.id = recipe.id; + + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Form( + key: _formKey, + child: Column( + children: [ + TextFormField( + initialValue: recipe.name, + onSaved: (value) { + _mutableRecipe.name = value; + }, + ), + RaisedButton( + onPressed: () { + if (_formKey.currentState.validate()) { + _formKey.currentState.save(); + + BlocProvider.of(context) + .add(RecipeUpdated(_mutableRecipe.toRecipe())); + } + }, + child: Text("Update"), + ) + ], + ), + ); + } +} diff --git a/lib/src/screens/recipe_edit_screen.dart b/lib/src/screens/recipe_edit_screen.dart new file mode 100644 index 00000000..59f935a9 --- /dev/null +++ b/lib/src/screens/recipe_edit_screen.dart @@ -0,0 +1,19 @@ +import 'package:flutter/material.dart'; +import 'package:nextcloud_cookbook_flutter/src/models/recipe.dart'; +import 'package:nextcloud_cookbook_flutter/src/screens/form/recipe_form.dart'; + +class RecipeEditScreen extends StatelessWidget { + final Recipe recipe; + + const RecipeEditScreen(this.recipe); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text("Edit Recipe"), + ), + body: RecipeForm(recipe), + ); + } +} From 91aac1fd5102aca69d3230a792ecba819c893d47 Mon Sep 17 00:00:00 2001 From: Linard Schwendener Date: Sat, 15 Aug 2020 00:15:57 +0200 Subject: [PATCH 07/17] Added MutableRecipe --- lib/src/models/recipe.dart | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/lib/src/models/recipe.dart b/lib/src/models/recipe.dart index d2c5b3f3..6d1bca32 100644 --- a/lib/src/models/recipe.dart +++ b/lib/src/models/recipe.dart @@ -70,3 +70,32 @@ class Recipe extends Equatable { @override List get props => [id]; } + +class MutableRecipe { + int id; + String name; + String imageUrl; + String recipeCategory; + String description; + List recipeIngredient; + List recipeInstructions; + int recipeYield; + Duration prepTime; + Duration cookTime; + Duration totalTime; + + Recipe toRecipe() { + return Recipe._( + id, + name, + imageUrl, + recipeCategory, + description, + recipeIngredient, + recipeInstructions, + recipeYield, + prepTime, + cookTime, + totalTime); + } +} From 68f4dd9605eba16cf89242ccb727e7f852c6671f Mon Sep 17 00:00:00 2001 From: Linard Schwendener Date: Sat, 15 Aug 2020 00:16:13 +0200 Subject: [PATCH 08/17] Added Navigation to new Edit Recipe --- lib/src/screens/login_page.dart | 3 +- lib/src/screens/recipe_screen.dart | 47 +++++++++++++++++++++++------- 2 files changed, 37 insertions(+), 13 deletions(-) diff --git a/lib/src/screens/login_page.dart b/lib/src/screens/login_page.dart index d7ee4583..82b15635 100644 --- a/lib/src/screens/login_page.dart +++ b/lib/src/screens/login_page.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; - import 'package:flutter_bloc/flutter_bloc.dart'; import '../blocs/authentication/authentication_bloc.dart'; @@ -13,7 +12,7 @@ class LoginPage extends StatelessWidget { appBar: AppBar( title: Text('Login'), ), - body: BlocProvider( + body: BlocProvider( create: (context) { return LoginBloc( authenticationBloc: BlocProvider.of(context), diff --git a/lib/src/screens/recipe_screen.dart b/lib/src/screens/recipe_screen.dart index dda93704..75d15b35 100644 --- a/lib/src/screens/recipe_screen.dart +++ b/lib/src/screens/recipe_screen.dart @@ -5,6 +5,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:nextcloud_cookbook_flutter/src/blocs/recipe/recipe.dart'; import 'package:nextcloud_cookbook_flutter/src/models/recipe.dart'; import 'package:nextcloud_cookbook_flutter/src/models/recipe_short.dart'; +import 'package:nextcloud_cookbook_flutter/src/screens/recipe_edit_screen.dart'; import 'package:nextcloud_cookbook_flutter/src/widget/authentication_cached_network_image.dart'; import 'package:nextcloud_cookbook_flutter/src/widget/duration_indicator.dart'; @@ -28,13 +29,34 @@ class RecipeScreenState extends State { @override Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text("Recipe"), - ), - body: BlocBuilder( - bloc: RecipeBloc()..add(RecipeLoaded(recipeId: recipeShort.recipeId)), + return BlocProvider( + create: (context) => + RecipeBloc()..add(RecipeLoaded(recipeId: recipeShort.recipeId)), + child: BlocBuilder( builder: (BuildContext context, RecipeState state) { + final recipeBloc = BlocProvider.of(context); + return Scaffold( + appBar: AppBar( + title: Text("Recipe"), + ), + floatingActionButton: FloatingActionButton( + onPressed: () { + if (state is RecipeLoadSuccess) { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) { + return BlocProvider.value( + value: recipeBloc, + child: RecipeEditScreen(state.recipe)); + }, + ), + ); + } + }, + child: Icon(Icons.edit), + ), + body: () { if (state is RecipeLoadSuccess) { return _buildRecipeScreen(state.recipe); } else if (state is RecipeLoadInProgress) { @@ -50,8 +72,10 @@ class RecipeScreenState extends State { child: Text("FAILED"), ); } - }, - )); + }(), + ); + }), + ); } Widget _buildRecipeScreen(Recipe recipe) { @@ -116,19 +140,20 @@ class RecipeScreenState extends State { ), Padding( padding: const EdgeInsets.only(bottom: 10.0), - child: Column( + child: Wrap( + alignment: WrapAlignment.center, + runSpacing: 10, + spacing: 10, children: [ (recipe.prepTime != null) ? DurationIndicator( duration: recipe.prepTime, name: "Preparation time") : SizedBox(height: 0), - SizedBox(height: 10), (recipe.cookTime != null) ? DurationIndicator( duration: recipe.cookTime, name: "Cooking time") : SizedBox(height: 0), - SizedBox(height: 10), (recipe.totalTime != null) ? DurationIndicator( duration: recipe.totalTime, name: "Total time") From acaf47056b7f4d7bf3a11310580b15645ac89717 Mon Sep 17 00:00:00 2001 From: Linard Schwendener Date: Sat, 15 Aug 2020 00:39:07 +0200 Subject: [PATCH 09/17] Recipe is now converted to JSON --- lib/src/models/recipe.dart | 16 ++++++++++++++++ lib/src/screens/recipe_edit_screen.dart | 15 ++++++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/lib/src/models/recipe.dart b/lib/src/models/recipe.dart index 6d1bca32..3033edda 100644 --- a/lib/src/models/recipe.dart +++ b/lib/src/models/recipe.dart @@ -1,8 +1,10 @@ import 'dart:convert'; import 'package:equatable/equatable.dart'; +import 'package:json_annotation/json_annotation.dart'; import 'package:nextcloud_cookbook_flutter/src/util/iso_time_format.dart'; +@JsonSerializable() class Recipe extends Equatable { final int id; final String name; @@ -67,6 +69,20 @@ class Recipe extends Equatable { totalTime); } + Map toJson() => { + 'id': id, + 'name': name, + 'imageUrl': imageUrl, + 'recipeCategory': recipeCategory, + 'description': description, + 'recipeIngredient': recipeIngredient, + 'recipeInstructions': recipeInstructions, + 'recipeYield': recipeYield, + 'prepTime': prepTime, + 'cookTime': cookTime, + 'totalTime': totalTime + }; + @override List get props => [id]; } diff --git a/lib/src/screens/recipe_edit_screen.dart b/lib/src/screens/recipe_edit_screen.dart index 59f935a9..41bb1935 100644 --- a/lib/src/screens/recipe_edit_screen.dart +++ b/lib/src/screens/recipe_edit_screen.dart @@ -1,4 +1,6 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:nextcloud_cookbook_flutter/src/blocs/recipe/recipe.dart'; import 'package:nextcloud_cookbook_flutter/src/models/recipe.dart'; import 'package:nextcloud_cookbook_flutter/src/screens/form/recipe_form.dart'; @@ -11,7 +13,18 @@ class RecipeEditScreen extends StatelessWidget { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text("Edit Recipe"), + title: BlocListener( + listener: (BuildContext context, RecipeState state) { + if (state is RecipeUpdateFailure) { + Scaffold.of(context).showSnackBar( + SnackBar( + content: Text("Update Failed: ${state.errorMsg}"), + backgroundColor: Colors.red, + ), + ); + } + }, + child: Text("Edit Recipe")), ), body: RecipeForm(recipe), ); From 4aa2a8b2e88ea3b12d25418b3dc05c86c840f044 Mon Sep 17 00:00:00 2001 From: Linard Schwendener Date: Sat, 15 Aug 2020 01:00:56 +0200 Subject: [PATCH 10/17] Added Update Service Provider --- lib/src/models/recipe.dart | 2 -- lib/src/services/data_repository.dart | 4 ++++ lib/src/services/recipe_provider.dart | 19 +++++++++++++++++++ pubspec.yaml | 3 +++ 4 files changed, 26 insertions(+), 2 deletions(-) diff --git a/lib/src/models/recipe.dart b/lib/src/models/recipe.dart index 3033edda..2d3e4af1 100644 --- a/lib/src/models/recipe.dart +++ b/lib/src/models/recipe.dart @@ -1,10 +1,8 @@ import 'dart:convert'; import 'package:equatable/equatable.dart'; -import 'package:json_annotation/json_annotation.dart'; import 'package:nextcloud_cookbook_flutter/src/util/iso_time_format.dart'; -@JsonSerializable() class Recipe extends Equatable { final int id; final String name; diff --git a/lib/src/services/data_repository.dart b/lib/src/services/data_repository.dart index f1927206..10b36c57 100644 --- a/lib/src/services/data_repository.dart +++ b/lib/src/services/data_repository.dart @@ -37,6 +37,10 @@ class DataRepository { return recipeProvider.fetchRecipe(id); } + Future updateRecipe(Recipe recipe) { + recipeProvider.updateRecipe(recipe); + } + Future> fetchCategories() { return categoriesProvider.fetchCategories(); } diff --git a/lib/src/services/recipe_provider.dart b/lib/src/services/recipe_provider.dart index 6f9f4ad2..83f0cc44 100644 --- a/lib/src/services/recipe_provider.dart +++ b/lib/src/services/recipe_provider.dart @@ -1,3 +1,4 @@ +import 'package:dio/dio.dart'; import 'package:http/http.dart'; import 'package:nextcloud_cookbook_flutter/src/models/app_authentication.dart'; import 'package:nextcloud_cookbook_flutter/src/models/recipe.dart'; @@ -27,4 +28,22 @@ class RecipeProvider { throw Exception("Failed to load RecipesShort!"); } } + + Future updateRecipe(Recipe recipe) async { + AppAuthentication appAuthentication = + UserRepository().currentAppAuthentication; + + try { + var response = await Dio().put( + "${appAuthentication.server}/index.php/apps/cookbook/api/recipes/${recipe.id}", + data: recipe.toJson(), + options: new Options( + contentType: "application/x-www-form-urlencoded", + headers: { + "authorization": appAuthentication.basicAuth, + })); + } catch (e) { + throw Exception(e); + } + } } diff --git a/pubspec.yaml b/pubspec.yaml index ac24a756..3cb7f0c9 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -42,6 +42,9 @@ dependencies: # Search flappy_search_bar: ^1.7.2 + # Form data HTTP Cliennt + dio: 3.0.10 + cached_network_image: 2.1.0+1 flutter_spinkit: "^2.1.0" From 90d8eebce5f6750b7a0a079a7d0922a6d5bb7fac Mon Sep 17 00:00:00 2001 From: Linard Schwendener Date: Sat, 15 Aug 2020 01:01:33 +0200 Subject: [PATCH 11/17] Actual Update Requests are made and work! --- lib/src/blocs/recipe/recipe_bloc.dart | 7 ++----- lib/src/blocs/recipe/recipe_state.dart | 4 +--- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/lib/src/blocs/recipe/recipe_bloc.dart b/lib/src/blocs/recipe/recipe_bloc.dart index 1b455e6d..99d7fb9f 100644 --- a/lib/src/blocs/recipe/recipe_bloc.dart +++ b/lib/src/blocs/recipe/recipe_bloc.dart @@ -1,5 +1,3 @@ -import 'dart:developer'; - import 'package:bloc/bloc.dart'; import 'package:nextcloud_cookbook_flutter/src/blocs/recipe/recipe.dart'; import 'package:nextcloud_cookbook_flutter/src/services/data_repository.dart'; @@ -34,9 +32,8 @@ class RecipeBloc extends Bloc { RecipeUpdated recipeUpdated) async* { try { yield RecipeUpdateInProgress(); - // TODO: add actual sending of data - log(recipeUpdated.recipe.toString()); - yield RecipeUpdateSuccess(null); + await dataRepository.updateRecipe(recipeUpdated.recipe); + yield RecipeUpdateSuccess(); } catch (_) { yield RecipeUpdateFailure(_.toString()); } diff --git a/lib/src/blocs/recipe/recipe_state.dart b/lib/src/blocs/recipe/recipe_state.dart index c2811e4b..11f78e8e 100644 --- a/lib/src/blocs/recipe/recipe_state.dart +++ b/lib/src/blocs/recipe/recipe_state.dart @@ -42,8 +42,6 @@ class RecipeUpdateFailure extends RecipeFailure { RecipeUpdateFailure(String errorMsg) : super(errorMsg); } -class RecipeUpdateSuccess extends RecipeSuccess { - RecipeUpdateSuccess(Recipe recipe) : super(recipe); -} +class RecipeUpdateSuccess extends RecipeState {} class RecipeUpdateInProgress extends RecipeState {} From 83da98c6c92cc1ed4162e5e1eac7705f5dcd6854 Mon Sep 17 00:00:00 2001 From: Linard Schwendener Date: Sat, 15 Aug 2020 01:17:34 +0200 Subject: [PATCH 12/17] Return On Update Successfull --- lib/src/blocs/recipe/recipe_bloc.dart | 4 +-- lib/src/blocs/recipe/recipe_state.dart | 9 +++++- lib/src/screens/recipe_edit_screen.dart | 43 ++++++++++++++++--------- lib/src/services/data_repository.dart | 4 +-- lib/src/services/recipe_provider.dart | 3 +- 5 files changed, 42 insertions(+), 21 deletions(-) diff --git a/lib/src/blocs/recipe/recipe_bloc.dart b/lib/src/blocs/recipe/recipe_bloc.dart index 99d7fb9f..328b5a86 100644 --- a/lib/src/blocs/recipe/recipe_bloc.dart +++ b/lib/src/blocs/recipe/recipe_bloc.dart @@ -32,8 +32,8 @@ class RecipeBloc extends Bloc { RecipeUpdated recipeUpdated) async* { try { yield RecipeUpdateInProgress(); - await dataRepository.updateRecipe(recipeUpdated.recipe); - yield RecipeUpdateSuccess(); + int recipeId = await dataRepository.updateRecipe(recipeUpdated.recipe); + yield RecipeUpdateSuccess(recipeId); } catch (_) { yield RecipeUpdateFailure(_.toString()); } diff --git a/lib/src/blocs/recipe/recipe_state.dart b/lib/src/blocs/recipe/recipe_state.dart index 11f78e8e..9d5bbead 100644 --- a/lib/src/blocs/recipe/recipe_state.dart +++ b/lib/src/blocs/recipe/recipe_state.dart @@ -42,6 +42,13 @@ class RecipeUpdateFailure extends RecipeFailure { RecipeUpdateFailure(String errorMsg) : super(errorMsg); } -class RecipeUpdateSuccess extends RecipeState {} +class RecipeUpdateSuccess extends RecipeState { + final int recipeId; + + const RecipeUpdateSuccess(this.recipeId); + + @override + List get props => [recipeId]; +} class RecipeUpdateInProgress extends RecipeState {} diff --git a/lib/src/screens/recipe_edit_screen.dart b/lib/src/screens/recipe_edit_screen.dart index 41bb1935..c0826e94 100644 --- a/lib/src/screens/recipe_edit_screen.dart +++ b/lib/src/screens/recipe_edit_screen.dart @@ -11,22 +11,35 @@ class RecipeEditScreen extends StatelessWidget { @override Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: BlocListener( - listener: (BuildContext context, RecipeState state) { - if (state is RecipeUpdateFailure) { - Scaffold.of(context).showSnackBar( - SnackBar( - content: Text("Update Failed: ${state.errorMsg}"), - backgroundColor: Colors.red, - ), - ); - } - }, - child: Text("Edit Recipe")), + return WillPopScope( + onWillPop: () { + RecipeBloc recipeBloc = BlocProvider.of(context); + if (recipeBloc.state is RecipeUpdateFailure) { + recipeBloc.add(RecipeLoaded(recipeId: recipe.id)); + } + return Future(() => true); + }, + child: Scaffold( + appBar: AppBar( + title: BlocListener( + listener: (BuildContext context, RecipeState state) { + if (state is RecipeUpdateFailure) { + Scaffold.of(context).showSnackBar( + SnackBar( + content: Text("Update Failed: ${state.errorMsg}"), + backgroundColor: Colors.red, + ), + ); + } else if (state is RecipeUpdateSuccess) { + BlocProvider.of(context) + .add(RecipeLoaded(recipeId: state.recipeId)); + Navigator.pop(context); + } + }, + child: Text("Edit Recipe")), + ), + body: RecipeForm(recipe), ), - body: RecipeForm(recipe), ); } } diff --git a/lib/src/services/data_repository.dart b/lib/src/services/data_repository.dart index 10b36c57..86d38223 100644 --- a/lib/src/services/data_repository.dart +++ b/lib/src/services/data_repository.dart @@ -37,8 +37,8 @@ class DataRepository { return recipeProvider.fetchRecipe(id); } - Future updateRecipe(Recipe recipe) { - recipeProvider.updateRecipe(recipe); + Future updateRecipe(Recipe recipe) { + return recipeProvider.updateRecipe(recipe); } Future> fetchCategories() { diff --git a/lib/src/services/recipe_provider.dart b/lib/src/services/recipe_provider.dart index 83f0cc44..d33010e9 100644 --- a/lib/src/services/recipe_provider.dart +++ b/lib/src/services/recipe_provider.dart @@ -29,7 +29,7 @@ class RecipeProvider { } } - Future updateRecipe(Recipe recipe) async { + Future updateRecipe(Recipe recipe) async { AppAuthentication appAuthentication = UserRepository().currentAppAuthentication; @@ -42,6 +42,7 @@ class RecipeProvider { headers: { "authorization": appAuthentication.basicAuth, })); + return response.data; } catch (e) { throw Exception(e); } From d739cab042471c7b79d00e96d975f5737da1efb2 Mon Sep 17 00:00:00 2001 From: Linard Schwendener Date: Sat, 15 Aug 2020 01:25:07 +0200 Subject: [PATCH 13/17] Added Recipe Yield --- lib/src/screens/form/recipe_form.dart | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/src/screens/form/recipe_form.dart b/lib/src/screens/form/recipe_form.dart index 421069a4..9d98f131 100644 --- a/lib/src/screens/form/recipe_form.dart +++ b/lib/src/screens/form/recipe_form.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:nextcloud_cookbook_flutter/src/blocs/recipe/recipe.dart'; import 'package:nextcloud_cookbook_flutter/src/models/recipe.dart'; +import 'package:validators/validators.dart'; class RecipeForm extends StatefulWidget { final Recipe recipe; @@ -34,10 +35,19 @@ class _RecipeFormState extends State { children: [ TextFormField( initialValue: recipe.name, + decoration: InputDecoration(hintText: "Recipe Name"), onSaved: (value) { _mutableRecipe.name = value; }, ), + TextFormField( + initialValue: recipe.recipeYield.toString(), + keyboardType: TextInputType.number, + decoration: InputDecoration(hintText: "Recipe Yield"), + validator: (value) => + isNumeric(value) ? null : "Recipe Yield should be a number", + onSaved: (value) => _mutableRecipe.recipeYield = int.parse(value), + ), RaisedButton( onPressed: () { if (_formKey.currentState.validate()) { From 931e5a04f7de64d492b60d81bed60772392b3051 Mon Sep 17 00:00:00 2001 From: Linard Schwendener Date: Sat, 15 Aug 2020 16:24:27 +0200 Subject: [PATCH 14/17] Added Missing fields to the model --- lib/src/models/recipe.dart | 54 +++++++++++++++++++++++++-- lib/src/screens/form/recipe_form.dart | 5 +-- pubspec.yaml | 2 +- 3 files changed, 53 insertions(+), 8 deletions(-) diff --git a/lib/src/models/recipe.dart b/lib/src/models/recipe.dart index 2d3e4af1..3ddfff7e 100644 --- a/lib/src/models/recipe.dart +++ b/lib/src/models/recipe.dart @@ -11,10 +11,14 @@ class Recipe extends Equatable { final String description; final List recipeIngredient; final List recipeInstructions; + final List tool; final int recipeYield; final Duration prepTime; final Duration cookTime; final Duration totalTime; + final String keywords; + final String image; + final String url; const Recipe._( this.id, @@ -24,10 +28,14 @@ class Recipe extends Equatable { this.description, this.recipeIngredient, this.recipeInstructions, + this.tool, this.recipeYield, this.prepTime, this.cookTime, - this.totalTime); + this.totalTime, + this.keywords, + this.image, + this.url); factory Recipe(String jsonString) { Map data = json.decode(jsonString); @@ -41,6 +49,7 @@ class Recipe extends Equatable { data["recipeIngredient"].cast().toList(); List recipeInstructions = data["recipeInstructions"].cast().toList(); + List tool = data["tool"].cast().toList(); int recipeYield = data["recipeYield"]; Duration prepTime = data.containsKey("prepTime") && data["prepTime"] != "" ? IsoTimeFormat.toDuration(data["prepTime"]) @@ -52,6 +61,9 @@ class Recipe extends Equatable { data.containsKey("totalTime") && data["totalTime"] != "" ? IsoTimeFormat.toDuration(data["totalTime"]) : null; + String keywords = data["keywords"]; + String image = data["image"]; + String url = data["url"]; return Recipe._( id, @@ -61,10 +73,14 @@ class Recipe extends Equatable { description, recipeIngredient, recipeInstructions, + tool, recipeYield, prepTime, cookTime, - totalTime); + totalTime, + keywords, + image, + url); } Map toJson() => { @@ -75,12 +91,34 @@ class Recipe extends Equatable { 'description': description, 'recipeIngredient': recipeIngredient, 'recipeInstructions': recipeInstructions, + 'tool': tool, 'recipeYield': recipeYield, 'prepTime': prepTime, 'cookTime': cookTime, - 'totalTime': totalTime + 'totalTime': totalTime, + 'keywords': keywords, + 'image': image, + 'url': url }; + MutableRecipe toMutableRecipe() { + MutableRecipe mutableRecipe = MutableRecipe(); + + mutableRecipe.id = this.id; + mutableRecipe.name = this.name; + mutableRecipe.imageUrl = this.imageUrl; + mutableRecipe.recipeCategory = this.recipeCategory; + mutableRecipe.description = this.description; + mutableRecipe.recipeIngredient = this.recipeIngredient; + mutableRecipe.recipeInstructions = this.recipeInstructions; + mutableRecipe.recipeYield = this.recipeYield; + mutableRecipe.prepTime = this.prepTime; + mutableRecipe.cookTime = this.cookTime; + mutableRecipe.totalTime = this.totalTime; + + return mutableRecipe; + } + @override List get props => [id]; } @@ -93,10 +131,14 @@ class MutableRecipe { String description; List recipeIngredient; List recipeInstructions; + List tool; int recipeYield; Duration prepTime; Duration cookTime; Duration totalTime; + String keywords; + String image; + String url; Recipe toRecipe() { return Recipe._( @@ -107,9 +149,13 @@ class MutableRecipe { description, recipeIngredient, recipeInstructions, + tool, recipeYield, prepTime, cookTime, - totalTime); + totalTime, + keywords, + image, + url); } } diff --git a/lib/src/screens/form/recipe_form.dart b/lib/src/screens/form/recipe_form.dart index 9d98f131..7ad007bc 100644 --- a/lib/src/screens/form/recipe_form.dart +++ b/lib/src/screens/form/recipe_form.dart @@ -16,13 +16,12 @@ class RecipeForm extends StatefulWidget { class _RecipeFormState extends State { final _formKey = GlobalKey(); Recipe recipe; - MutableRecipe _mutableRecipe = MutableRecipe(); + MutableRecipe _mutableRecipe; @override void initState() { recipe = widget.recipe; - - _mutableRecipe.id = recipe.id; + _mutableRecipe = recipe.toMutableRecipe(); super.initState(); } diff --git a/pubspec.yaml b/pubspec.yaml index 3cb7f0c9..b1fd4db0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -14,7 +14,7 @@ description: A new Flutter application. version: 0.3.1+7 environment: - sdk: ">=2.1.0 <3.0.0" + sdk: ">=2.2.0 <3.0.0" dependencies: flutter: From 57ea5904b0ee638368c936899596dc0a2637fd8c Mon Sep 17 00:00:00 2001 From: Linard Schwendener Date: Sat, 15 Aug 2020 17:29:39 +0200 Subject: [PATCH 15/17] Reworked Recipe Screen --- lib/src/screens/recipe_screen.dart | 203 +++++++++++++++++------------ pubspec.yaml | 2 +- 2 files changed, 121 insertions(+), 84 deletions(-) diff --git a/lib/src/screens/recipe_screen.dart b/lib/src/screens/recipe_screen.dart index 75d15b35..6fa206e0 100644 --- a/lib/src/screens/recipe_screen.dart +++ b/lib/src/screens/recipe_screen.dart @@ -1,4 +1,3 @@ -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -8,6 +7,7 @@ import 'package:nextcloud_cookbook_flutter/src/models/recipe_short.dart'; import 'package:nextcloud_cookbook_flutter/src/screens/recipe_edit_screen.dart'; import 'package:nextcloud_cookbook_flutter/src/widget/authentication_cached_network_image.dart'; import 'package:nextcloud_cookbook_flutter/src/widget/duration_indicator.dart'; +import 'package:url_launcher/url_launcher.dart'; class RecipeScreen extends StatefulWidget { final RecipeShort recipeShort; @@ -63,7 +63,7 @@ class RecipeScreenState extends State { return Center( child: CircularProgressIndicator(), ); - } else if (state is RecipeLoadFailure) { + } else if (state is RecipeFailure) { return Center( child: Text(state.errorMsg), ); @@ -121,21 +121,36 @@ class RecipeScreenState extends State { ), Padding( padding: const EdgeInsets.only(bottom: 10.0), - child: RichText( - text: TextSpan( - text: "Servings: ", - style: TextStyle( - color: Colors.black, fontWeight: FontWeight.bold), - children: [ - TextSpan( - text: recipe.recipeYield.toString(), + child: Row( + children: [ + RichText( + text: TextSpan( + text: "Servings: ", style: TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold), + children: [ + TextSpan( + text: recipe.recipeYield.toString(), + style: TextStyle( // color: Colors.black, - fontWeight: FontWeight.w400, - ), + fontWeight: FontWeight.w400, + ), + ) + ], + ), + ), + Spacer(), + if (recipe.url.isNotEmpty) + RaisedButton( + onPressed: () async { + if (await canLaunch(recipe.url)) { + await launch(recipe.url); + } + }, + child: Text("Source"), ) - ], - ), + ], ), ), Padding( @@ -145,83 +160,105 @@ class RecipeScreenState extends State { runSpacing: 10, spacing: 10, children: [ - (recipe.prepTime != null) - ? DurationIndicator( - duration: recipe.prepTime, - name: "Preparation time") - : SizedBox(height: 0), - (recipe.cookTime != null) - ? DurationIndicator( - duration: recipe.cookTime, name: "Cooking time") - : SizedBox(height: 0), - (recipe.totalTime != null) - ? DurationIndicator( - duration: recipe.totalTime, name: "Total time") - : SizedBox(height: 0), + if (recipe.prepTime != null) + DurationIndicator( + duration: recipe.prepTime, + name: "Preparation time"), + if (recipe.cookTime != null) + DurationIndicator( + duration: recipe.cookTime, name: "Cooking time"), + if (recipe.totalTime != null) + DurationIndicator( + duration: recipe.totalTime, name: "Total time"), ], ), ), - Padding( - padding: const EdgeInsets.only(bottom: 10.0), - child: Text( - "Ingredients:", - style: TextStyle(fontWeight: FontWeight.bold), + if (recipe.tool.isNotEmpty) + Padding( + padding: const EdgeInsets.only(bottom: 10.0), + child: ExpansionTile( + title: Text("Tools"), + children: [ + Align( + alignment: Alignment.centerLeft, + child: Padding( + padding: const EdgeInsets.only(left: 15.0), + child: Text(recipe.tool.fold( + "", (p, e) => p + "- " + e.trim() + "\n")), + ), + ), + ], + ), + ), + if (recipe.recipeIngredient.isNotEmpty) + Padding( + padding: const EdgeInsets.only(bottom: 10.0), + child: ExpansionTile( + title: Text("Ingredients"), + children: [ + Align( + alignment: Alignment.centerLeft, + child: Padding( + padding: const EdgeInsets.only(left: 15.0), + child: Text(recipe.recipeIngredient.fold( + "", (p, e) => p + "- " + e.trim() + "\n")), + ), + ), + ], + ), ), - ), - Padding( - padding: const EdgeInsets.only(bottom: 10.0), - child: Text(recipe.recipeIngredient - .fold("", (p, e) => p + e + "\n")), - ), Padding( padding: const EdgeInsets.only(bottom: 10.0), - child: Text( - "Instructions:", - style: TextStyle(fontWeight: FontWeight.bold), + child: ExpansionTile( + title: Text("Instructions"), + initiallyExpanded: true, + children: [ + Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: ListView.separated( + shrinkWrap: true, + physics: ClampingScrollPhysics(), + itemBuilder: (context, index) { + return GestureDetector( + onTap: () { + setState(() { + instructionsDone[index] = + !instructionsDone[index]; + }); + }, + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: 40, + height: 40, + margin: + EdgeInsets.only(right: 15, top: 10), + child: instructionsDone[index] + ? Icon(Icons.check) + : Center(child: Text("${index + 1}")), + decoration: ShapeDecoration( + shape: CircleBorder( + side: + BorderSide(color: Colors.grey)), + color: Colors.grey[300], + ), + ), + Expanded( + child: Text( + recipe.recipeInstructions[index]), + ), + ], + ), + ); + }, + separatorBuilder: (c, i) => SizedBox(height: 10), + itemCount: recipe.recipeInstructions.length, + ), + ), + ], ), ), - Column( - mainAxisSize: MainAxisSize.min, - children: [ - ListView.separated( - shrinkWrap: true, - physics: ClampingScrollPhysics(), - itemBuilder: (context, index) { - return GestureDetector( - onTap: () { - setState(() { - instructionsDone[index] = - !instructionsDone[index]; - }); - }, - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - width: 40, - height: 40, - margin: EdgeInsets.only(right: 15, top: 10), - child: instructionsDone[index] - ? Icon(Icons.check) - : Center(child: Text("${index + 1}")), - decoration: ShapeDecoration( - shape: CircleBorder( - side: BorderSide(color: Colors.grey)), - color: Colors.grey[300], - ), - ), - Expanded( - child: Text(recipe.recipeInstructions[index]), - ), - ], - ), - ); - }, - separatorBuilder: (c, i) => SizedBox(height: 10), - itemCount: recipe.recipeInstructions.length, - ), - ], - ) ], ), ), diff --git a/pubspec.yaml b/pubspec.yaml index b1fd4db0..20619a0d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -14,7 +14,7 @@ description: A new Flutter application. version: 0.3.1+7 environment: - sdk: ">=2.2.0 <3.0.0" + sdk: ">=2.6.0 <3.0.0" dependencies: flutter: From a28033415d9541eea9ab2a6bbd0831e29be4a87a Mon Sep 17 00:00:00 2001 From: Linard Schwendener Date: Sat, 15 Aug 2020 17:31:08 +0200 Subject: [PATCH 16/17] Temporary Disable Edit button until feature can be used --- lib/src/screens/recipe_screen.dart | 35 +++++++++++++++--------------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/lib/src/screens/recipe_screen.dart b/lib/src/screens/recipe_screen.dart index 6fa206e0..2e401a5c 100644 --- a/lib/src/screens/recipe_screen.dart +++ b/lib/src/screens/recipe_screen.dart @@ -4,7 +4,6 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:nextcloud_cookbook_flutter/src/blocs/recipe/recipe.dart'; import 'package:nextcloud_cookbook_flutter/src/models/recipe.dart'; import 'package:nextcloud_cookbook_flutter/src/models/recipe_short.dart'; -import 'package:nextcloud_cookbook_flutter/src/screens/recipe_edit_screen.dart'; import 'package:nextcloud_cookbook_flutter/src/widget/authentication_cached_network_image.dart'; import 'package:nextcloud_cookbook_flutter/src/widget/duration_indicator.dart'; import 'package:url_launcher/url_launcher.dart'; @@ -39,23 +38,23 @@ class RecipeScreenState extends State { appBar: AppBar( title: Text("Recipe"), ), - floatingActionButton: FloatingActionButton( - onPressed: () { - if (state is RecipeLoadSuccess) { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) { - return BlocProvider.value( - value: recipeBloc, - child: RecipeEditScreen(state.recipe)); - }, - ), - ); - } - }, - child: Icon(Icons.edit), - ), +// floatingActionButton: FloatingActionButton( +// onPressed: () { +// if (state is RecipeLoadSuccess) { +// Navigator.push( +// context, +// MaterialPageRoute( +// builder: (context) { +// return BlocProvider.value( +// value: recipeBloc, +// child: RecipeEditScreen(state.recipe)); +// }, +// ), +// ); +// } +// }, +// child: Icon(Icons.edit), +// ), body: () { if (state is RecipeLoadSuccess) { return _buildRecipeScreen(state.recipe); From f7cd859cab9d9840e29627931f16abb5eb94b8bc Mon Sep 17 00:00:00 2001 From: Linard Schwendener Date: Sat, 15 Aug 2020 17:33:40 +0200 Subject: [PATCH 17/17] Updated Version Number --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 20619a0d..1f29790c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -11,7 +11,7 @@ description: A new Flutter application. # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 0.3.1+7 +version: 0.3.2+8 environment: sdk: ">=2.6.0 <3.0.0"