diff options
Diffstat (limited to 'lib/views')
-rw-r--r-- | lib/views/image_view.dart | 28 | ||||
-rw-r--r-- | lib/views/recipe_view.dart | 136 | ||||
-rw-r--r-- | lib/views/settings_view.dart | 5 | ||||
-rw-r--r-- | lib/views/shoplist_view.dart | 2 | ||||
-rw-r--r-- | lib/views/vote_view.dart | 3 |
5 files changed, 115 insertions, 59 deletions
diff --git a/lib/views/image_view.dart b/lib/views/image_view.dart index 8721d5e..615aa26 100644 --- a/lib/views/image_view.dart +++ b/lib/views/image_view.dart @@ -4,7 +4,6 @@ import 'package:flutter/material.dart'; import 'package:kulinar_app/constants.dart'; -// TODO: IMPLEMENT: zooming and multiple images possible class ImageView extends StatelessWidget { const ImageView({Key? key, required this.image}) : super(key: key); @@ -12,27 +11,28 @@ class ImageView extends StatelessWidget { @override Widget build(BuildContext context) { - return Scaffold( - backgroundColor: _averageImageColor(image), - body: Center( - child: Container( - child: GestureDetector( - child: Hero( - tag: "image", - child: Image( - image: FileImage(File(image)), + return InteractiveViewer( + child: Scaffold( + backgroundColor: _averageImageColor(image), + body: Center( + child: Container( + child: GestureDetector( + child: Hero( + tag: "image", + child: Image( + image: FileImage(File(image)), + ), ), + onTap: () { + Navigator.pop(context); + }, ), - onTap: () { - Navigator.pop(context); - }, ), ), ), ); } - // TODO: IMPLEMENT: average color background Color _averageImageColor(String path) { return cPrimaryColor; } diff --git a/lib/views/recipe_view.dart b/lib/views/recipe_view.dart index 960ba84..a0304c3 100644 --- a/lib/views/recipe_view.dart +++ b/lib/views/recipe_view.dart @@ -1,9 +1,9 @@ import 'dart:io'; import 'package:flutter/material.dart'; -import 'package:http/http.dart'; import 'package:kulinar_app/constants.dart'; +import 'package:kulinar_app/models/data/shoplist_data_class.dart'; import 'package:kulinar_app/views/image_view.dart'; import 'package:kulinar_app/models/recipe_class.dart'; import 'package:kulinar_app/widgets/toastbar_widget.dart'; @@ -12,10 +12,13 @@ import 'package:kulinar_app/widgets/page_route_transitions.dart'; import 'package:kulinar_app/widgets/utility_icon_row_widget.dart'; import 'package:kulinar_app/models/data/settings_data_class.dart'; -import 'package:image_picker/image_picker.dart'; +import 'package:flutter_markdown/flutter_markdown.dart'; import 'package:path_provider/path_provider.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:image_picker/image_picker.dart'; import 'package:share_plus/share_plus.dart'; +import 'package:http/http.dart'; + +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; class RecipeView extends StatefulWidget { const RecipeView({Key? key, this.remote = false, required this.readonly, this.recipe, this.redrawCallback, this.showToastCallback}) : super(key: key); @@ -108,7 +111,7 @@ class _RecipeViewState extends State<RecipeView> { } } - void _removeRecipe() { + void _removeRecipe(BuildContext _) { Navigator.pop(context); RecipeData.recipeList.remove(widget.recipe); RecipeData.save(); @@ -118,11 +121,12 @@ class _RecipeViewState extends State<RecipeView> { String _content = AppLocalizations.of(context)!.removed; String _actionLabel = AppLocalizations.of(context)!.undo; - // FIXME: This might throw due to "!" - widget.showToastCallback!(_content, _actionLabel, () { - _addRecipe(passedRecipe: _removedRecipe); - widget.redrawCallback!(); - }); + if (widget.showToastCallback != null) { + widget.showToastCallback!(_content, _actionLabel, () { + _addRecipe(passedRecipe: _removedRecipe); + widget.redrawCallback!(); + }); + } setState(() {}); } @@ -142,12 +146,10 @@ class _RecipeViewState extends State<RecipeView> { setState(() {}); } - // FIXME: doesnt refresh, should be passed from parent void _pickImage() async { XFile? _pickedFile; if (SettingsData.settings["photoSource"] == "0") { - // TODO: Maybe pick multi image? _pickedFile = await ImagePicker().pickImage(source: ImageSource.camera); } else { _pickedFile = await ImagePicker().pickImage(source: ImageSource.gallery); @@ -167,7 +169,6 @@ class _RecipeViewState extends State<RecipeView> { setState(() {}); } - // FIXME: doesnt refresh, should be passed from parent void _removeImage() async { _unsavedRecipe.image = null; await RecipeData.save(); @@ -202,7 +203,7 @@ class _RecipeViewState extends State<RecipeView> { padding: const EdgeInsets.only(right: 20.0, left: 20.0), child: TextFormField( controller: _controller1, - style: cRecipeTextStyle, + style: cRecipeTitleStyle, cursorColor: cPrimaryColor, enabled: _isEditingAllowed(), focusNode: _detailsTitleFocus, @@ -226,7 +227,6 @@ class _RecipeViewState extends State<RecipeView> { Widget _buildImage() { if (_unsavedRecipe.image != null) { - // TODO: Use https://api.flutter.dev/flutter/widgets/InteractiveViewer-class.html return Padding( padding: const EdgeInsets.only(top: 12.0, left: 20.0, right: 20.0), child: GestureDetector( @@ -257,34 +257,92 @@ class _RecipeViewState extends State<RecipeView> { Widget _buildDescriptionInput() { return Padding( padding: const EdgeInsets.all(18.0), - child: TextField( - expands: true, - maxLines: null, - minLines: null, - decoration: null, - controller: _controller2, - cursorColor: cPrimaryColor, - focusNode: _detailsTextFocus, - style: cRecipeDescriptionStyle, - readOnly: !_isEditingAllowed(), - enableInteractiveSelection: true, - toolbarOptions: ToolbarOptions( - copy: true, - cut: true, - paste: true, - selectAll: true, + child: _isEditingAllowed() ? _buildInputDescriptuion() : _buildMarkdownDescription(), + ); + } + + TextField _buildInputDescriptuion() { + return TextField( + expands: true, + maxLines: null, + minLines: null, + decoration: null, + controller: _controller2, + cursorColor: cPrimaryColor, + focusNode: _detailsTextFocus, + style: cRecipeDescriptionStyle, + enableInteractiveSelection: true, + toolbarOptions: ToolbarOptions( + copy: true, + cut: true, + paste: true, + selectAll: true, + ), + ); + } + + MarkdownBody _buildMarkdownDescription() { + return MarkdownBody( + data: _controller2.text, + selectable: true, + onTapLink: (text, href, title) => null, + imageBuilder: (uri, title, alt) => Container(), + styleSheet: MarkdownStyleSheet( + tableBody: cRecipeDescriptionStyle, + a: cRecipeDescriptionStyle, + p: cRecipeDescriptionStyle, + h1: cRecipeSubtitleStyle, + h2: cRecipeSubtitleStyle, + h3: cRecipeSubtitleStyle, + h4: cRecipeSubtitleStyle, + h5: cRecipeSubtitleStyle, + h6: cRecipeSubtitleStyle, +/* TODO: remove code and quote block + blockquoteDecoration: BoxDecoration(color: Color.fromARGB(255, 255, 13, 13)), + code: cDetailsTextStyle, +*/ + listIndent: 15.0, + listBullet: cRecipeDescriptionStyle, + listBulletPadding: EdgeInsets.only(right: 10.0), + horizontalRuleDecoration: BoxDecoration( + border: Border.all( + color: Colors.grey, + width: 0.5, + ), ), ), ); } - void _shareData() { + void _shareData(BuildContext _) { Share.share("${_unsavedRecipe.title}\n\n${_unsavedRecipe.description}", subject: _unsavedRecipe.title); } - void _addToShoppingList() {} + void _addToShoppingList(BuildContext _) { + final _ingredients = _unsavedRecipe.description!.split("\n")..retainWhere((element) => element.startsWith("- ")); + + for (String _ingredient in _ingredients) { + String _item = _ingredient.substring(2).trim(); + + if (!ShoplistData.shoplist.contains(_item)) { + ShoplistData.shoplist.add(_item); + } + } + + ShoplistData.save(); + + if (widget.showToastCallback != null) { + // TODO: translation + widget.showToastCallback!("Added to Shoplist", "Ok", () { + _addRecipe(passedRecipe: _removedRecipe); + widget.redrawCallback!(); + }); + } + } void _uploadRecipe(BuildContext context) async { + return; + Map<String, String> _headers = {"Content-Type": "application/json; charset=UTF-8"}; String _body = _unsavedRecipe.toJsonString(); Response? res; @@ -310,13 +368,12 @@ class _RecipeViewState extends State<RecipeView> { List<Widget> _localActions = [ _buildAppBarCheckActions(), PopupMenuButton( - onSelected: (int value) => _actionList.elementAt(value)(), + onSelected: (int value) => _actionList.elementAt(value)(context), itemBuilder: (BuildContext context) => [ - // TODO: Translations - PopupMenuItem<int>(value: 0, child: Text("Teilen")), - PopupMenuItem<int>(value: 1, child: Text("Einkaufen")), - PopupMenuItem<int>(value: 2, child: Text("Hochladen")), - PopupMenuItem<int>(value: 3, child: Text("Löschen")), + PopupMenuItem<int>(value: 0, child: Text(AppLocalizations.of(context)!.menu1)), + PopupMenuItem<int>(value: 1, child: Text(AppLocalizations.of(context)!.menu2)), + PopupMenuItem<int>(value: 2, child: Text(AppLocalizations.of(context)!.menu3)), + PopupMenuItem<int>(value: 3, child: Text(AppLocalizations.of(context)!.menu4)), ], ), ]; @@ -333,12 +390,13 @@ class _RecipeViewState extends State<RecipeView> { List<Widget> _remoteLookupAction = [ IconButton( icon: Icon(Icons.menu_open_rounded), - onPressed: null, // TODO: IMPLEMENT: Show local recipe + onPressed: null, ), ]; if (widget.remote && RecipeData.recipeList.contains(recipe)) return _remoteLookupAction; if (widget.remote) return _remoteDownloadAction; + return _localActions; } diff --git a/lib/views/settings_view.dart b/lib/views/settings_view.dart index 7f2ae90..d217bb7 100644 --- a/lib/views/settings_view.dart +++ b/lib/views/settings_view.dart @@ -15,7 +15,6 @@ class SettingsView extends StatefulWidget { _SettingsViewState createState() => _SettingsViewState(); } -// TODO: Add subtitles class _SettingsViewState extends State<SettingsView> { final TextEditingController _controller = TextEditingController(); final FocusNode _focusNode = FocusNode(); @@ -54,7 +53,7 @@ class _SettingsViewState extends State<SettingsView> { title: Text(AppLocalizations.of(context)!.setting22, style: cDefaultTextStyle), trailing: IconButton( icon: Icon(Icons.arrow_forward_rounded), - onPressed: null, // TODO: IMPLEMENT: specific server settings + onPressed: null, ), ), _buildTinyTitle(AppLocalizations.of(context)!.settingTitle3), @@ -93,7 +92,7 @@ class _SettingsViewState extends State<SettingsView> { ); } - void _updateSettings(String text) { + void _updateSettings(String? text) { _focusNode.unfocus(); if (text != null) { diff --git a/lib/views/shoplist_view.dart b/lib/views/shoplist_view.dart index a9caed8..500b8fc 100644 --- a/lib/views/shoplist_view.dart +++ b/lib/views/shoplist_view.dart @@ -113,7 +113,7 @@ class _ShoplistView extends State<ShoplistView> { padding: const EdgeInsets.all(15.0), child: TextFormField( controller: _controller, - style: cRecipeTextStyle.copyWith(color: cIconColor), + style: cRecipeTitleStyle.copyWith(color: cIconColor), cursorColor: cIconColor, focusNode: _focusNode, textInputAction: TextInputAction.done, diff --git a/lib/views/vote_view.dart b/lib/views/vote_view.dart index 7de2d10..86ddc0b 100644 --- a/lib/views/vote_view.dart +++ b/lib/views/vote_view.dart @@ -12,14 +12,13 @@ class VoteView extends StatefulWidget { _VoteViewState createState() => _VoteViewState(); } -// TODO: Change from vote to schedule class _VoteViewState extends State<VoteView> { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text(AppLocalizations.of(context)!.category6)), drawer: CustomDrawer(initalIndex: 3), - body: NoContentError(), // TODO: IMPLEMENT: A voting system + body: NoContentError(), ); } } |