From 5dd9fba073ac50e8688086e20befa6544cfa2e87 Mon Sep 17 00:00:00 2001 From: Eric Windmill Date: Tue, 16 Apr 2024 10:36:42 -0400 Subject: [PATCH] fix analysis issues in ai_recipe_generation --- ai_recipe_generation/lib/app_bar.dart | 24 +++---- .../lib/features/prompt/prompt_model.dart | 17 +++-- .../lib/features/prompt/prompt_screen.dart | 43 +++++------ .../widgets/app_info_dialog_widget.dart | 20 +++--- .../widgets/full_prompt_dialog_widget.dart | 12 ++-- .../prompt/widgets/image_input_widget.dart | 25 ++++--- .../widgets/prompt_text_input_widget.dart | 51 ------------- .../recipes/saved_recipes_screen.dart | 36 +++++----- .../widgets/recipe_display_widget.dart | 45 ++++++------ ai_recipe_generation/lib/main.dart | 8 +-- ai_recipe_generation/lib/router.dart | 19 ++--- .../lib/services/firestore.dart | 11 +-- ai_recipe_generation/lib/services/gemini.dart | 4 +- .../lib/services/gemini_talk.dart | 71 ------------------- ai_recipe_generation/lib/theme.dart | 16 ++--- ai_recipe_generation/lib/util/markdown.dart | 27 ------- .../lib/util/tap_recorder.dart | 2 +- .../lib/widgets/add_image_widget.dart | 9 ++- .../widgets/filter_chip_selection_input.dart | 18 ++--- .../highlight_border_on_hover_widget.dart | 5 +- .../lib/widgets/icon_loading_indicator.dart | 4 +- .../widgets/marketplace_button_widget.dart | 12 ++-- .../lib/widgets/prompt_image_widget.dart | 6 +- ai_recipe_generation/pubspec.yaml | 2 + ai_recipe_generation/test/markdown_test.dart | 34 --------- 25 files changed, 167 insertions(+), 354 deletions(-) delete mode 100644 ai_recipe_generation/lib/features/prompt/widgets/prompt_text_input_widget.dart delete mode 100644 ai_recipe_generation/lib/services/gemini_talk.dart delete mode 100644 ai_recipe_generation/lib/util/markdown.dart delete mode 100644 ai_recipe_generation/test/markdown_test.dart diff --git a/ai_recipe_generation/lib/app_bar.dart b/ai_recipe_generation/lib/app_bar.dart index b0bbdf78d..27ed982b5 100644 --- a/ai_recipe_generation/lib/app_bar.dart +++ b/ai_recipe_generation/lib/app_bar.dart @@ -54,7 +54,7 @@ class AnimatedAppBar extends StatelessWidget { expandedHeight: expandedHeight, collapsedHeight: collapsedHeight, backgroundColor: Theme.of(context).primaryColor, - shape: AppBarShapeBorder(50), + shape: const AppBarShapeBorder(50), title: Column( children: [ Row( @@ -68,7 +68,7 @@ class AnimatedAppBar extends StatelessWidget { semanticsLabel: 'Chef cat icon', ), ), - SizedBox( + const SizedBox( width: MarketplaceTheme.spacing1, ), if (scrollController.positions.isNotEmpty && @@ -83,15 +83,15 @@ class AnimatedAppBar extends StatelessWidget { headerText, style: MarketplaceTheme.heading3, ), - Spacer(), + const Spacer(), if (scrollController.positions.isNotEmpty && scrollController.offset > 200) IconButton( - onPressed: () => showDialog( + onPressed: () => showDialog( context: context, - builder: (context) => AppInfoDialog(), + builder: (context) => const AppInfoDialog(), ), - icon: Icon( + icon: const Icon( Symbols.info, color: Colors.black12, ), @@ -102,7 +102,7 @@ class AnimatedAppBar extends StatelessWidget { ), flexibleSpace: FlexibleSpaceBar( background: Padding( - padding: EdgeInsets.all(MarketplaceTheme.spacing4), + padding: const EdgeInsets.all(MarketplaceTheme.spacing4), child: SizedBox( width: MediaQuery.of(context).size.width, child: Row( @@ -118,12 +118,12 @@ class AnimatedAppBar extends StatelessWidget { ), IconButton( onPressed: () { - showDialog( + showDialog( context: context, - builder: (context) => AppInfoDialog(), + builder: (context) => const AppInfoDialog(), ); }, - icon: Icon( + icon: const Icon( Symbols.info, color: Colors.black12, ), @@ -134,7 +134,7 @@ class AnimatedAppBar extends StatelessWidget { ), ), bottom: PreferredSize( - preferredSize: Size(double.infinity, 0), + preferredSize: const Size(double.infinity, 0), child: Align( alignment: Alignment.centerLeft, child: Padding( @@ -144,7 +144,7 @@ class AnimatedAppBar extends StatelessWidget { : MarketplaceTheme.spacing1, ), child: AnimatedDefaultTextStyle( - duration: Duration(milliseconds: 0), + duration: const Duration(milliseconds: 0), style: textStyle, child: Text( headerText, diff --git a/ai_recipe_generation/lib/features/prompt/prompt_model.dart b/ai_recipe_generation/lib/features/prompt/prompt_model.dart index 42a5359e4..8ff1222d4 100644 --- a/ai_recipe_generation/lib/features/prompt/prompt_model.dart +++ b/ai_recipe_generation/lib/features/prompt/prompt_model.dart @@ -6,10 +6,10 @@ class PromptData { PromptData({ required this.images, required this.textInput, - basicIngredients, - cuisines, - dietaryRestrictions, - additionalTextInputs, + Set? basicIngredients, + Set? cuisines, + Set? dietaryRestrictions, + List? additionalTextInputs, }) : additionalTextInputs = additionalTextInputs ?? [], selectedBasicIngredients = basicIngredients ?? {}, selectedCuisines = cuisines ?? {}, @@ -52,16 +52,15 @@ class PromptData { List? additionalTextInputs, Set? basicIngredients, Set? cuisineSelections, - Set? dietaryRestrictions, + Set? dietaryRestrictions, }) { return PromptData( images: images ?? this.images, textInput: textInput ?? this.textInput, additionalTextInputs: additionalTextInputs ?? this.additionalTextInputs, - basicIngredients: basicIngredients ?? this.selectedBasicIngredients, - cuisines: cuisineSelections ?? this.selectedCuisines, - dietaryRestrictions: - dietaryRestrictions ?? this.selectedDietaryRestrictions, + basicIngredients: basicIngredients ?? selectedBasicIngredients, + cuisines: cuisineSelections ?? selectedCuisines, + dietaryRestrictions: dietaryRestrictions ?? selectedDietaryRestrictions, ); } } diff --git a/ai_recipe_generation/lib/features/prompt/prompt_screen.dart b/ai_recipe_generation/lib/features/prompt/prompt_screen.dart index 2343eb7e6..f42520d92 100644 --- a/ai_recipe_generation/lib/features/prompt/prompt_screen.dart +++ b/ai_recipe_generation/lib/features/prompt/prompt_screen.dart @@ -31,17 +31,17 @@ class PromptScreen extends StatelessWidget { builder: (context, constraints) { return SingleChildScrollView( physics: canScroll - ? BouncingScrollPhysics() - : NeverScrollableScrollPhysics(), + ? const BouncingScrollPhysics() + : const NeverScrollableScrollPhysics(), child: Container( padding: constraints.isMobile - ? EdgeInsets.only( + ? const EdgeInsets.only( left: MarketplaceTheme.spacing7, right: MarketplaceTheme.spacing7, bottom: MarketplaceTheme.spacing7, top: MarketplaceTheme.spacing7, ) - : EdgeInsets.only( + : const EdgeInsets.only( left: MarketplaceTheme.spacing7, right: MarketplaceTheme.spacing7, bottom: MarketplaceTheme.spacing1, @@ -51,7 +51,7 @@ class PromptScreen extends StatelessWidget { decoration: BoxDecoration( color: Colors.white, border: Border.all(color: MarketplaceTheme.borderColor), - borderRadius: BorderRadius.only( + borderRadius: const BorderRadius.only( topLeft: Radius.circular(4), topRight: Radius.circular(50), bottomRight: @@ -199,7 +199,7 @@ class PromptScreen extends StatelessWidget { padding: const EdgeInsets.all(elementPadding), child: _TextField( controller: viewModel.promptTextController, - onChanged: (String value) { + onChanged: (value) { viewModel.notify(); }, ), @@ -212,7 +212,7 @@ class PromptScreen extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceAround, crossAxisAlignment: CrossAxisAlignment.center, children: [ - if (!constraints.isMobile) Spacer(flex: 1), + if (!constraints.isMobile) const Spacer(flex: 1), if (!constraints.isMobile) Expanded( flex: 3, @@ -226,13 +226,13 @@ class PromptScreen extends StatelessWidget { MarketplaceTheme.secondary.withOpacity(.1), ), ), - Spacer(flex: 1), + const Spacer(flex: 1), Expanded( flex: constraints.isMobile ? 10 : 3, child: MarketplaceButton( onPressed: () { final promptData = viewModel.buildPrompt(); - showDialog( + showDialog( context: context, builder: (context) { return FullPromptDialog( @@ -245,12 +245,12 @@ class PromptScreen extends StatelessWidget { icon: Symbols.info_rounded, ), ), - Spacer(flex: 1), + const Spacer(flex: 1), Expanded( flex: constraints.isMobile ? 10 : 3, child: MarketplaceButton( onPressed: () async { - viewModel.submitPrompt().then((_) async { + await viewModel.submitPrompt().then((_) async { if (viewModel.recipe != null) { bool? shouldSave = await showDialog( context: context, @@ -278,7 +278,7 @@ class PromptScreen extends StatelessWidget { icon: Symbols.send, ), ), - Spacer(flex: 1), + const Spacer(flex: 1), ], ), ), @@ -294,7 +294,7 @@ class PromptScreen extends StatelessWidget { hoverColor: MarketplaceTheme.secondary.withOpacity(.1), ), ), - SizedBox(height: 200.0), + const SizedBox(height: 200.0), ], ), ), @@ -307,7 +307,6 @@ class PromptScreen extends StatelessWidget { class _FilterChipSection extends StatelessWidget { const _FilterChipSection({ - super.key, required this.child, required this.label, }); @@ -351,38 +350,34 @@ class _FilterChipSection extends StatelessWidget { class _TextField extends StatelessWidget { const _TextField({ - super.key, required this.controller, this.onChanged, }); final TextEditingController controller; - final Function(String)? onChanged; + final Null Function(String)? onChanged; @override Widget build(BuildContext context) { return TextField( - scrollPadding: EdgeInsets.only(bottom: 150), + scrollPadding: const EdgeInsets.only(bottom: 150), maxLines: null, onChanged: onChanged, minLines: 3, controller: controller, - onTapOutside: (event) { - // FocusManager.instance.primaryFocus?.unfocus(); - }, - style: MaterialStateTextStyle.resolveWith( + style: WidgetStateTextStyle.resolveWith( (states) => MarketplaceTheme.dossierParagraph), decoration: InputDecoration( fillColor: Theme.of(context).splashColor, hintText: "Add additional context...", - hintStyle: MaterialStateTextStyle.resolveWith( + hintStyle: WidgetStateTextStyle.resolveWith( (states) => MarketplaceTheme.dossierParagraph, ), - enabledBorder: OutlineInputBorder( + enabledBorder: const OutlineInputBorder( borderRadius: BorderRadius.zero, borderSide: BorderSide(width: 1, color: Colors.black12), ), - focusedBorder: OutlineInputBorder( + focusedBorder: const OutlineInputBorder( borderRadius: BorderRadius.zero, borderSide: BorderSide(width: 1, color: Colors.black45), ), diff --git a/ai_recipe_generation/lib/features/prompt/widgets/app_info_dialog_widget.dart b/ai_recipe_generation/lib/features/prompt/widgets/app_info_dialog_widget.dart index fa40ba19a..5aa2a2ae0 100644 --- a/ai_recipe_generation/lib/features/prompt/widgets/app_info_dialog_widget.dart +++ b/ai_recipe_generation/lib/features/prompt/widgets/app_info_dialog_widget.dart @@ -11,7 +11,7 @@ class AppInfoDialog extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Icon(icon ?? Symbols.label_important_outline), - SizedBox( + const SizedBox( width: 10, ), Expanded( @@ -43,35 +43,35 @@ class AppInfoDialog extends StatelessWidget { "Use the form on this screen to ask Cat Chef to make a recipe for you.", style: MarketplaceTheme.heading3, ), - SizedBox( + const SizedBox( height: MarketplaceTheme.spacing4, ), bulletRow( "Add images of ingredients you have, like a picture of the inside of your fridge or pantry.", icon: Symbols.looks_one, ), - SizedBox( + const SizedBox( height: MarketplaceTheme.spacing7, ), bulletRow( "Choose what kind of food you're in the mood for, and what staple ingredients you have that might not be pictured.", icon: Symbols.looks_two, ), - SizedBox( + const SizedBox( height: MarketplaceTheme.spacing7, ), bulletRow( "In the text box at the bottom, add any additional context that you'd like. \nFor example, you could say \"I'm in a hurry! Make sure the recipe doesn't take longer than 30 minutes to make.\"", icon: Symbols.looks_3, ), - SizedBox( + const SizedBox( height: MarketplaceTheme.spacing7, ), bulletRow( "Submit the prompt, and Chef Noodle will give you a recipe!", icon: Symbols.looks_4, ), - SizedBox( + const SizedBox( height: MarketplaceTheme.spacing4, ), Text( @@ -80,7 +80,7 @@ class AppInfoDialog extends StatelessWidget { ), const SizedBox(height: MarketplaceTheme.spacing4), TextButton.icon( - icon: Icon( + icon: const Icon( Symbols.close, color: Colors.black87, ), @@ -92,9 +92,9 @@ class AppInfoDialog extends StatelessWidget { Navigator.pop(context); }, style: ButtonStyle( - shape: MaterialStateProperty.resolveWith( + shape: WidgetStateProperty.resolveWith( (states) { - return RoundedRectangleBorder( + return const RoundedRectangleBorder( side: BorderSide(color: Colors.black26), borderRadius: BorderRadius.all( Radius.circular(MarketplaceTheme.defaultBorderRadius), @@ -102,7 +102,7 @@ class AppInfoDialog extends StatelessWidget { ); }, ), - textStyle: MaterialStateTextStyle.resolveWith( + textStyle: WidgetStateTextStyle.resolveWith( (states) { return MarketplaceTheme.dossierParagraph .copyWith(color: Colors.black45); diff --git a/ai_recipe_generation/lib/features/prompt/widgets/full_prompt_dialog_widget.dart b/ai_recipe_generation/lib/features/prompt/widgets/full_prompt_dialog_widget.dart index 20b9c7bdf..420667c06 100644 --- a/ai_recipe_generation/lib/features/prompt/widgets/full_prompt_dialog_widget.dart +++ b/ai_recipe_generation/lib/features/prompt/widgets/full_prompt_dialog_widget.dart @@ -15,7 +15,7 @@ class FullPromptDialog extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Icon(icon ?? Symbols.label_important_outline), - SizedBox( + const SizedBox( width: 10, ), Expanded( @@ -47,7 +47,7 @@ class FullPromptDialog extends StatelessWidget { if (promptData.images.isNotEmpty) Container( height: 100, - decoration: BoxDecoration( + decoration: const BoxDecoration( border: Border.symmetric( horizontal: BorderSide( color: MarketplaceTheme.borderColor, @@ -73,7 +73,7 @@ class FullPromptDialog extends StatelessWidget { ...promptData.additionalTextInputs.map((i) => bulletRow(i)), const SizedBox(height: MarketplaceTheme.spacing4), TextButton.icon( - icon: Icon( + icon: const Icon( Symbols.close, color: Colors.black87, ), @@ -85,9 +85,9 @@ class FullPromptDialog extends StatelessWidget { Navigator.pop(context); }, style: ButtonStyle( - shape: MaterialStateProperty.resolveWith( + shape: WidgetStateProperty.resolveWith( (states) { - return RoundedRectangleBorder( + return const RoundedRectangleBorder( side: BorderSide(color: Colors.black26), borderRadius: BorderRadius.all( Radius.circular(MarketplaceTheme.defaultBorderRadius), @@ -95,7 +95,7 @@ class FullPromptDialog extends StatelessWidget { ); }, ), - textStyle: MaterialStateTextStyle.resolveWith( + textStyle: WidgetStateTextStyle.resolveWith( (states) { return MarketplaceTheme.dossierParagraph .copyWith(color: Colors.black45); diff --git a/ai_recipe_generation/lib/features/prompt/widgets/image_input_widget.dart b/ai_recipe_generation/lib/features/prompt/widgets/image_input_widget.dart index 8437458ed..5d4ac9270 100644 --- a/ai_recipe_generation/lib/features/prompt/widgets/image_input_widget.dart +++ b/ai_recipe_generation/lib/features/prompt/widgets/image_input_widget.dart @@ -13,7 +13,7 @@ import '../../../widgets/prompt_image_widget.dart'; import '../prompt_view_model.dart'; class AddImageToPromptWidget extends StatefulWidget { - AddImageToPromptWidget({ + const AddImageToPromptWidget({ super.key, this.width = 100, this.height = 100, @@ -50,13 +50,13 @@ class _AddImageToPromptWidgetState extends State { transitionBuilder: (context, animation, secondaryAnimation, child) { return AnimatedOpacity( opacity: animation.value, - duration: Duration(milliseconds: 100), + duration: const Duration(milliseconds: 100), child: child, ); }, pageBuilder: (context, animation, secondaryAnimation) { return Dialog.fullscreen( - insetAnimationDuration: Duration(seconds: 1), + insetAnimationDuration: const Duration(seconds: 1), child: FutureBuilder( future: _initializeControllerFuture, builder: (context, snapshot) { @@ -169,7 +169,7 @@ class _CameraViewState extends State { @override Widget build(BuildContext context) { - CameraController _controller = widget.controller; + CameraController controller = widget.controller; return Stack( children: [ Center( @@ -179,11 +179,11 @@ class _CameraViewState extends State { child: FittedBox( fit: BoxFit.cover, child: SizedBox( - height: _controller.value.previewSize!.width, - width: _controller.value.previewSize!.height, + height: controller.value.previewSize!.width, + width: controller.value.previewSize!.height, child: Center( child: CameraPreview( - _controller, + controller, // child: ElevatedButton( // child: Text('Button'), // onPressed: () {}, @@ -215,7 +215,7 @@ class _CameraViewState extends State { color: flashOn ? Colors.yellowAccent : Colors.white, ), onPressed: () { - _controller.setFlashMode( + controller.setFlashMode( flashOn ? FlashMode.off : FlashMode.always); setState(() { flashOn = !flashOn; @@ -227,7 +227,7 @@ class _CameraViewState extends State { padding: const EdgeInsets.only(right: MarketplaceTheme.spacing4), child: IconButton( - icon: Icon( + icon: const Icon( Symbols.cancel, color: Colors.white, size: 40, @@ -252,7 +252,7 @@ class _CameraViewState extends State { mainAxisAlignment: MainAxisAlignment.center, children: [ IconButton( - icon: Icon( + icon: const Icon( Symbols.camera, color: Colors.white, size: 70, @@ -260,11 +260,11 @@ class _CameraViewState extends State { onPressed: () async { try { await widget.initializeControllerFuture; - final image = await _controller.takePicture(); + final image = await controller.takePicture(); if (!context.mounted) return; Navigator.of(context).pop(image); } catch (e) { - print(e); + rethrow; } }, ), @@ -274,6 +274,5 @@ class _CameraViewState extends State { ) ], ); - ; } } diff --git a/ai_recipe_generation/lib/features/prompt/widgets/prompt_text_input_widget.dart b/ai_recipe_generation/lib/features/prompt/widgets/prompt_text_input_widget.dart deleted file mode 100644 index 6d242f570..000000000 --- a/ai_recipe_generation/lib/features/prompt/widgets/prompt_text_input_widget.dart +++ /dev/null @@ -1,51 +0,0 @@ -import 'package:flutter/material.dart'; - -class PromptTextInput extends StatelessWidget { - PromptTextInput({ - super.key, - required this.onChanged, - required this.onSendPressed, - textEditingController, - }) : _controller = textEditingController ?? TextEditingController(); - - final ValueChanged onChanged; - final VoidCallback onSendPressed; - final TextEditingController _controller; - - @override - Widget build(BuildContext context) { - return Container( - decoration: BoxDecoration( - border: Border.all(color: Colors.transparent), - borderRadius: BorderRadius.circular(40), - color: Colors.white, - ), - padding: const EdgeInsets.all(8.0), - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Container( - constraints: BoxConstraints( - maxWidth: MediaQuery.of(context).size.width - 110, - ), - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 8.0), - child: TextField( - maxLines: null, - controller: _controller, - decoration: InputDecoration( - border: InputBorder.none, - hintText: "Additional context...", - ), - onChanged: (value) { - onChanged(value); - }, - ), - ), - ), - ], - ), - ); - } -} diff --git a/ai_recipe_generation/lib/features/recipes/saved_recipes_screen.dart b/ai_recipe_generation/lib/features/recipes/saved_recipes_screen.dart index 480823e8f..c85db608e 100644 --- a/ai_recipe_generation/lib/features/recipes/saved_recipes_screen.dart +++ b/ai_recipe_generation/lib/features/recipes/saved_recipes_screen.dart @@ -29,20 +29,20 @@ class _SavedRecipesScreenState extends State builder: (context, constraints) { return Padding( padding: constraints.isMobile - ? EdgeInsets.only( + ? const EdgeInsets.only( left: MarketplaceTheme.spacing7, right: MarketplaceTheme.spacing7, bottom: MarketplaceTheme.spacing7, top: MarketplaceTheme.spacing7, ) - : EdgeInsets.only( + : const EdgeInsets.only( left: MarketplaceTheme.spacing7, right: MarketplaceTheme.spacing7, bottom: MarketplaceTheme.spacing1, top: MarketplaceTheme.spacing7, ), child: ClipRRect( - borderRadius: BorderRadius.only( + borderRadius: const BorderRadius.only( topLeft: Radius.circular(MarketplaceTheme.defaultBorderRadius), topRight: Radius.circular(50), bottomRight: @@ -52,7 +52,7 @@ class _SavedRecipesScreenState extends State child: Container( decoration: BoxDecoration( border: Border.all(color: MarketplaceTheme.borderColor), - borderRadius: BorderRadius.only( + borderRadius: const BorderRadius.only( topLeft: Radius.circular(MarketplaceTheme.defaultBorderRadius), topRight: Radius.circular(50), @@ -66,8 +66,8 @@ class _SavedRecipesScreenState extends State child: constraints.isMobile ? ListView.builder( physics: widget.canScroll - ? PageScrollPhysics() - : NeverScrollableScrollPhysics(), + ? const PageScrollPhysics() + : const NeverScrollableScrollPhysics(), itemCount: viewModel.recipes.length, itemBuilder: (context, idx) { final recipe = viewModel.recipes[idx]; @@ -76,7 +76,7 @@ class _SavedRecipesScreenState extends State child: Align( heightFactor: .5, child: Padding( - padding: EdgeInsets.symmetric( + padding: const EdgeInsets.symmetric( horizontal: MarketplaceTheme.spacing7, vertical: MarketplaceTheme.spacing7, ), @@ -97,15 +97,15 @@ class _SavedRecipesScreenState extends State ) : GridView.count( physics: widget.canScroll - ? PageScrollPhysics() - : NeverScrollableScrollPhysics(), + ? const PageScrollPhysics() + : const NeverScrollableScrollPhysics(), crossAxisCount: 3, childAspectRatio: 1.5, children: [ ...List.generate(viewModel.recipes.length, (idx) { final recipe = viewModel.recipes[idx]; return Padding( - padding: EdgeInsets.symmetric( + padding: const EdgeInsets.symmetric( horizontal: MarketplaceTheme.spacing7, vertical: MarketplaceTheme.spacing7, ), @@ -128,7 +128,7 @@ class _SavedRecipesScreenState extends State } class _ListTile extends StatefulWidget { - _ListTile({ + const _ListTile({ super.key, required this.recipe, this.idx = 0, @@ -158,7 +158,7 @@ class _ListTileState extends State<_ListTile> { return GestureDetector( child: HighlightBorderOnHoverWidget( - borderRadius: BorderRadius.only( + borderRadius: const BorderRadius.only( topLeft: Radius.circular(MarketplaceTheme.defaultBorderRadius), topRight: Radius.circular(50), bottomRight: Radius.circular(MarketplaceTheme.defaultBorderRadius), @@ -166,7 +166,7 @@ class _ListTileState extends State<_ListTile> { ), color: color, child: Container( - decoration: BoxDecoration( + decoration: const BoxDecoration( boxShadow: [ BoxShadow( offset: Offset(0, -2), @@ -185,7 +185,7 @@ class _ListTileState extends State<_ListTile> { ), child: Container( decoration: BoxDecoration( - borderRadius: BorderRadius.only( + borderRadius: const BorderRadius.only( topLeft: Radius.circular(MarketplaceTheme.defaultBorderRadius), topRight: Radius.circular(50), bottomRight: @@ -195,7 +195,7 @@ class _ListTileState extends State<_ListTile> { ), color: color.withOpacity(.3), ), - padding: EdgeInsets.all(MarketplaceTheme.spacing7), + padding: const EdgeInsets.all(MarketplaceTheme.spacing7), child: Stack( children: [ Text( @@ -225,15 +225,15 @@ class _ListTileState extends State<_ListTile> { ), ), onTap: () async { - await showDialog( + await showDialog( context: context, builder: (context) { return RecipeDialogScreen( recipe: widget.recipe, subheading: Row( children: [ - Text('My rating:'), - SizedBox(width: 10), + const Text('My rating:'), + const SizedBox(width: 10), StartRating( initialRating: widget.recipe.rating, starColor: MarketplaceTheme.tertiary, diff --git a/ai_recipe_generation/lib/features/recipes/widgets/recipe_display_widget.dart b/ai_recipe_generation/lib/features/recipes/widgets/recipe_display_widget.dart index e44c99eaa..780697a21 100644 --- a/ai_recipe_generation/lib/features/recipes/widgets/recipe_display_widget.dart +++ b/ai_recipe_generation/lib/features/recipes/widgets/recipe_display_widget.dart @@ -24,11 +24,11 @@ class RecipeDisplayWidget extends StatelessWidget { Row( mainAxisSize: MainAxisSize.min, children: [ - Icon( + const Icon( Symbols.stat_0_rounded, size: 12, ), - SizedBox( + const SizedBox( width: 5, ), Expanded( @@ -52,7 +52,7 @@ class RecipeDisplayWidget extends StatelessWidget { if (instructions.first.startsWith(RegExp('[0-9]'))) { for (var instruction in instructions) { widgets.add(Text(instruction)); - widgets.add(SizedBox(height: MarketplaceTheme.spacing6)); + widgets.add(const SizedBox(height: MarketplaceTheme.spacing6)); } } else { for (var i = 0; i < instructions.length; i++) { @@ -60,7 +60,7 @@ class RecipeDisplayWidget extends StatelessWidget { '${i + 1}. ${instructions[i]}', softWrap: true, )); - widgets.add(SizedBox(height: MarketplaceTheme.spacing6)); + widgets.add(const SizedBox(height: MarketplaceTheme.spacing6)); } } @@ -70,12 +70,12 @@ class RecipeDisplayWidget extends StatelessWidget { @override Widget build(BuildContext context) { return SingleChildScrollView( - physics: ClampingScrollPhysics(), + physics: const ClampingScrollPhysics(), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( - padding: EdgeInsets.all(MarketplaceTheme.defaultBorderRadius), + padding: const EdgeInsets.all(MarketplaceTheme.defaultBorderRadius), color: MarketplaceTheme.primary.withOpacity(.5), child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -104,24 +104,24 @@ class RecipeDisplayWidget extends StatelessWidget { ), TextButton( style: ButtonStyle( - backgroundColor: - MaterialStateColor.resolveWith((states) { - if (states.contains(MaterialState.hovered)) { + backgroundColor: WidgetStateColor.resolveWith((states) { + if (states.contains(WidgetState.hovered)) { return MarketplaceTheme.scrim.withOpacity(.6); } return Colors.white; }), - shape: MaterialStateProperty.resolveWith( + shape: WidgetStateProperty.resolveWith( (states) { return RoundedRectangleBorder( - side: BorderSide(color: MarketplaceTheme.primary), + side: const BorderSide( + color: MarketplaceTheme.primary), borderRadius: BorderRadius.circular( MarketplaceTheme.defaultBorderRadius, ), ); }, ), - textStyle: MaterialStateTextStyle.resolveWith( + textStyle: WidgetStateTextStyle.resolveWith( (states) { return MarketplaceTheme.dossierParagraph.copyWith( color: Colors.black45, @@ -130,7 +130,7 @@ class RecipeDisplayWidget extends StatelessWidget { ), ), onPressed: () async { - await showDialog( + await showDialog( context: context, builder: (context) { return AlertDialog( @@ -144,7 +144,7 @@ class RecipeDisplayWidget extends StatelessWidget { ); }, child: Transform.translate( - offset: Offset(0, 5), + offset: const Offset(0, 5), child: Padding( padding: const EdgeInsets.symmetric( vertical: MarketplaceTheme.spacing6), @@ -159,7 +159,7 @@ class RecipeDisplayWidget extends StatelessWidget { ), ), Transform.translate( - offset: Offset(1, -6), + offset: const Offset(1, -6), child: Transform.rotate( angle: -pi / 20.0, child: Text( @@ -175,12 +175,12 @@ class RecipeDisplayWidget extends StatelessWidget { ) ], ), - Divider( + const Divider( height: 40, color: Colors.black26, ), Table( - columnWidths: { + columnWidths: const { 0: FlexColumnWidth(2), 1: FlexColumnWidth(3), }, @@ -212,17 +212,17 @@ class RecipeDisplayWidget extends StatelessWidget { fontWeight: FontWeight.bold, ), ), - Text(''), + const Text(''), ]), ...recipe.nutritionInformation.entries.map((entry) { return TableRow(children: [ Row( children: [ - Icon( + const Icon( Symbols.stat_0_rounded, size: 12, ), - SizedBox( + const SizedBox( width: 5, ), Expanded( @@ -234,7 +234,8 @@ class RecipeDisplayWidget extends StatelessWidget { ), ], ), - Text(entry.value, style: MarketplaceTheme.label) + Text(entry.value as String, + style: MarketplaceTheme.label) ]); }), ], @@ -258,7 +259,7 @@ class RecipeDisplayWidget extends StatelessWidget { Text('Ingredients:', style: MarketplaceTheme.subheading1), ), ..._buildIngredients(recipe.ingredients), - SizedBox(height: MarketplaceTheme.spacing4), + const SizedBox(height: MarketplaceTheme.spacing4), Padding( padding: const EdgeInsets.symmetric( vertical: MarketplaceTheme.spacing7), diff --git a/ai_recipe_generation/lib/main.dart b/ai_recipe_generation/lib/main.dart index 36199a9dd..034a776f7 100644 --- a/ai_recipe_generation/lib/main.dart +++ b/ai_recipe_generation/lib/main.dart @@ -28,11 +28,11 @@ void main() async { camera = cameras.first; } - runApp(MainApp()); + runApp(const MainApp()); } class MainApp extends StatefulWidget { - MainApp({super.key}); + const MainApp({super.key}); @override State createState() => _MainAppState(); @@ -105,7 +105,7 @@ class _MainAppState extends State { child: MaterialApp( debugShowCheckedModeBanner: false, theme: MarketplaceTheme.theme, - scrollBehavior: ScrollBehavior().copyWith( + scrollBehavior: const ScrollBehavior().copyWith( dragDevices: { PointerDeviceKind.mouse, PointerDeviceKind.touch, @@ -113,7 +113,7 @@ class _MainAppState extends State { PointerDeviceKind.unknown, }, ), - home: AdaptiveRouter(), + home: const AdaptiveRouter(), ), ), ), diff --git a/ai_recipe_generation/lib/router.dart b/ai_recipe_generation/lib/router.dart index b2d663175..5ab4b7da4 100644 --- a/ai_recipe_generation/lib/router.dart +++ b/ai_recipe_generation/lib/router.dart @@ -50,7 +50,8 @@ class _AdaptiveRouterState extends State innerScrollAllowed = scrollController.offset >= 230; if (scrollController.offset >= 230) { scrollController.animateTo(230, - duration: Duration(milliseconds: 100), curve: Curves.decelerate); + duration: const Duration(milliseconds: 100), + curve: Curves.decelerate); } // Don't change the text opacity if scrolling down from original position (overscroll) @@ -87,11 +88,11 @@ class _AdaptiveRouterState extends State } List destinations = [ - NavigationRailDestination( + const NavigationRailDestination( icon: Icon(Symbols.home), label: Text('Create a recipe'), ), - NavigationRailDestination( + const NavigationRailDestination( icon: Icon(Symbols.bookmarks), label: Text('Saved Recipes'), ) @@ -143,21 +144,21 @@ class _AdaptiveRouterState extends State child: Container( height: bottomTabBarHeight, decoration: ShapeDecoration( - shadows: [ + shadows: const [ BoxShadow( offset: Offset(1, -1), color: Colors.black45, blurRadius: 5, ) ], - shape: BottomBarShapeBorder(50), + shape: const BottomBarShapeBorder(50), color: Theme.of(context).primaryColor, ), child: TabBar( labelColor: Colors.black, unselectedLabelColor: Colors.black26, controller: tabController, - onTap: (int idx) { + onTap: (idx) { setState(() {}); }, dividerColor: Colors.transparent, @@ -174,7 +175,7 @@ class _AdaptiveRouterState extends State height: 160, width: 160, child: IconLoadingAnimator( - icons: [ + icons: const [ Symbols.icecream, Symbols.local_pizza, Symbols.restaurant_menu, @@ -204,7 +205,7 @@ class _AdaptiveRouterState extends State decoration: BoxDecoration( borderRadius: BorderRadius.circular( MarketplaceTheme.defaultBorderRadius), - boxShadow: [ + boxShadow: const [ BoxShadow( offset: Offset(-1, 1), color: Colors.black45, @@ -218,7 +219,7 @@ class _AdaptiveRouterState extends State ), ), child: Padding( - padding: EdgeInsets.all(MarketplaceTheme.spacing6), + padding: const EdgeInsets.all(MarketplaceTheme.spacing6), child: Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ diff --git a/ai_recipe_generation/lib/services/firestore.dart b/ai_recipe_generation/lib/services/firestore.dart index d2debf247..2a1e599c8 100644 --- a/ai_recipe_generation/lib/services/firestore.dart +++ b/ai_recipe_generation/lib/services/firestore.dart @@ -6,15 +6,18 @@ const recipePath = '/recipes'; final firestore = FirebaseFirestore.instance; class FirestoreService { - static saveRecipe(Recipe recipe) { - firestore.collection(recipePath).doc(recipe.id).set(recipe.toFirestore()); + static Future saveRecipe(Recipe recipe) async { + await firestore + .collection(recipePath) + .doc(recipe.id) + .set(recipe.toFirestore()); } - static deleteRecipe(Recipe recipe) async { + static Future deleteRecipe(Recipe recipe) async { await firestore.doc("$recipePath/${recipe.id}").delete(); } - static updateRecipe(Recipe recipe) async { + static Future updateRecipe(Recipe recipe) async { await firestore .doc("$recipePath/${recipe.id}") .update(recipe.toFirestore()); diff --git a/ai_recipe_generation/lib/services/gemini.dart b/ai_recipe_generation/lib/services/gemini.dart index bc38a223f..56c2a6fb2 100644 --- a/ai_recipe_generation/lib/services/gemini.dart +++ b/ai_recipe_generation/lib/services/gemini.dart @@ -2,8 +2,6 @@ import 'package:google_generative_ai/google_generative_ai.dart'; import '../features/prompt/prompt_model.dart'; -/// DONT USE THIS CLASS IN THE TALK. -/// It wasn't written to accommodate copy+pasting Gemini code. class GeminiService { static Future generateContent( GenerativeModel model, PromptData prompt) async { @@ -19,7 +17,7 @@ class GeminiService { final mainText = TextPart(prompt.textInput); final additionalTextParts = prompt.additionalTextInputs.map((t) => TextPart(t)); - final imagesParts = []; + final imagesParts = []; for (var f in prompt.images) { final bytes = await (f.readAsBytes()); diff --git a/ai_recipe_generation/lib/services/gemini_talk.dart b/ai_recipe_generation/lib/services/gemini_talk.dart deleted file mode 100644 index fcc857525..000000000 --- a/ai_recipe_generation/lib/services/gemini_talk.dart +++ /dev/null @@ -1,71 +0,0 @@ -import 'package:google_generative_ai/google_generative_ai.dart'; - -import '../features/prompt/prompt_model.dart'; - -/// This code is for visual purposes only. I never actually use it when the app is running. - -const formatting = ''' -Return the recipe as valid JSON using the following structure: -{ - "id": \$uniqueId, - "title": \$recipeTitle, - "ingredients": \$ingredients, - "description": \$description, - "instructions": \$instructions, - "cuisine": \$cuisineType, - "allergens": \$allergens, - "servings": \$servings, - "nutritionInformation": { - "calories": "\$calories", - "fat": "\$fat", - "carbohydrates": "\$carbohydrates", - "protein": "\$protein", - }, -} - -uniqueId should be unique and of type String. -title, description, cuisine, allergens, and servings should be of String type. -ingredients and instructions should be of type List. -nutritionInformation should be of type Map. -'''; - -class GenerativeAIService { - static Future generateContent( - GenerativeModel model, - PromptData prompt, - ) async { - final imagesParts = []; - for (var f in prompt.images) { - final bytes = await (f.readAsBytes()); - imagesParts.add(DataPart('image/jpeg', bytes)); - } - - return await model.generateContent( - [ - Content.multi([ - TextPart(''' -You are a Cat who's a chef that travels around the world a lot, and your travels inspire recipes. - -Recommend a recipe for me based on the provided image. -The recipe should only contain real, edible ingredients. -If the image or images attached don't contain any food items, respond to say that you cannot recommend a recipe with inedible ingredients. -Adhere to food safety and handling best practices like ensuring that poultry is fully cooked. - -I'm in the mood for the following types of cuisine: ${prompt.cuisines}, - -I have the following dietary restrictions: ${prompt.selectedDietaryRestrictions} - -Optionally also include the following ingredients: ${prompt.ingredients} - -After providing the recipe, explain creatively why the recipe is good based on only the ingredients used in the recipe. Tell a short story of a travel experience that inspired the recipe. -List out any ingredients that are potential allergens. -Provide a summary of how many people the recipe will serve and the the nutritional information per serving. -'''), - for (var text in prompt.additionalTextInputs) TextPart(text), - TextPart(formatting), - ...imagesParts, - ]) - ], - ); - } -} diff --git a/ai_recipe_generation/lib/theme.dart b/ai_recipe_generation/lib/theme.dart index 3a028b6a7..1d546de95 100644 --- a/ai_recipe_generation/lib/theme.dart +++ b/ai_recipe_generation/lib/theme.dart @@ -4,15 +4,15 @@ import 'package:google_fonts/google_fonts.dart'; abstract class MarketplaceTheme { static ThemeData theme = ThemeData( fontFamily: GoogleFonts.lexend().fontFamily, - textTheme: GoogleFonts.lexendTextTheme() - .copyWith() - .apply(bodyColor: Color(0xff000000), displayColor: Color(0xff000000)), - colorScheme: ColorScheme.light( + textTheme: GoogleFonts.lexendTextTheme().copyWith().apply( + bodyColor: const Color(0xff000000), + displayColor: const Color(0xff000000)), + colorScheme: const ColorScheme.light( primary: Color(0xffA2E3F6), secondary: Color(0xff4FAD85), tertiary: Color(0xffDE7A60), scrim: Color(0xffFFABC7), - background: Color(0xffFDF7F0), + surface: Color(0xffFDF7F0), onSecondary: Color(0xff000000), shadow: Color(0xffAEAEAE), onPrimary: Color(0xffFFFFFF), @@ -20,14 +20,14 @@ abstract class MarketplaceTheme { useMaterial3: true, canvasColor: Colors.transparent, navigationBarTheme: NavigationBarThemeData( - indicatorColor: Color(0xffA2E3F6), + indicatorColor: const Color(0xffA2E3F6), indicatorShape: CircleBorder( side: BorderSide.lerp( - BorderSide( + const BorderSide( color: Color(0xff000000), width: 2, ), - BorderSide( + const BorderSide( color: Color(0xff000000), width: 2, ), diff --git a/ai_recipe_generation/lib/util/markdown.dart b/ai_recipe_generation/lib/util/markdown.dart deleted file mode 100644 index 1f336aae7..000000000 --- a/ai_recipe_generation/lib/util/markdown.dart +++ /dev/null @@ -1,27 +0,0 @@ -import 'dart:convert'; - -/// Matches a complete title: 0-10 whitespaces, followed by 1-6 #s, followed by the title text -final markdownHeadingMatcher = - RegExp(r'^ {0,10}(#{1,6})(?:[ \x09\x0b\x0c].*?)?(?:\s(#*)\s*)?$'); - -/// Matches the #s only -final headingSyntaxMatcher = RegExp(r'(#{1,6})'); - -/// This is VERY naive. For demo purposes only. -(bool, String) getTitleFromMarkdownRecipe(String markdown) { - final LineSplitter splitter = LineSplitter(); - final mdCopy = markdown; - final mdAsLines = splitter.convert(mdCopy); - final matchingLine = mdAsLines.firstWhere((line) { - RegExpMatch? match = markdownHeadingMatcher.firstMatch(line); - return match != null; - }); - RegExpMatch? match = markdownHeadingMatcher.firstMatch(matchingLine); - if (match != null) { - final split = matchingLine.split(headingSyntaxMatcher); - if (split.length == 1) return (true, split.first); - return (true, split.last); - } - - return (false, markdown); -} diff --git a/ai_recipe_generation/lib/util/tap_recorder.dart b/ai_recipe_generation/lib/util/tap_recorder.dart index 833d50f6c..e3bf8cc69 100644 --- a/ai_recipe_generation/lib/util/tap_recorder.dart +++ b/ai_recipe_generation/lib/util/tap_recorder.dart @@ -28,7 +28,7 @@ const _tapRadius = 15.0, /// /// It will both visualize them and add them to [recordedTaps]. class TapRecorder extends SingleChildRenderObjectWidget { - const TapRecorder({Key? key, required Widget child}) : super(child: child); + const TapRecorder({super.key, required Widget child}) : super(child: child); @override RenderObject createRenderObject(BuildContext context) { diff --git a/ai_recipe_generation/lib/widgets/add_image_widget.dart b/ai_recipe_generation/lib/widgets/add_image_widget.dart index 0d921ee25..5fa2efa85 100644 --- a/ai_recipe_generation/lib/widgets/add_image_widget.dart +++ b/ai_recipe_generation/lib/widgets/add_image_widget.dart @@ -1,11 +1,10 @@ -import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:material_symbols_icons/symbols.dart'; import '../theme.dart'; class AddImage extends StatefulWidget { - AddImage({ + const AddImage({ super.key, required this.onTap, this.height = 100, @@ -39,12 +38,12 @@ class _AddImageState extends State { @override Widget build(BuildContext context) { return MouseRegion( - onEnter: (PointerEnterEvent event) { + onEnter: (event) { setState(() { hovered = true; }); }, - onExit: (PointerExitEvent event) { + onExit: (event) { setState(() { hovered = false; }); @@ -71,7 +70,7 @@ class _AddImageState extends State { decoration: BoxDecoration( color: buttonColor, ), - child: Center( + child: const Center( child: Icon( Symbols.add_photo_alternate_rounded, size: 32, diff --git a/ai_recipe_generation/lib/widgets/filter_chip_selection_input.dart b/ai_recipe_generation/lib/widgets/filter_chip_selection_input.dart index dd5fcd4e8..88354caaa 100644 --- a/ai_recipe_generation/lib/widgets/filter_chip_selection_input.dart +++ b/ai_recipe_generation/lib/widgets/filter_chip_selection_input.dart @@ -4,14 +4,14 @@ import 'package:gemini_io_talk/util/extensions.dart'; import 'package:gemini_io_talk/util/filter_chip_enums.dart'; class FilterChipSelectionInput extends StatefulWidget { - FilterChipSelectionInput({ + const FilterChipSelectionInput({ super.key, required this.onChipSelected, required this.selectedValues, required this.allValues, }); - final Function(Set) onChipSelected; + final Null Function(Set) onChipSelected; final Set selectedValues; final List allValues; @@ -34,9 +34,9 @@ class _CategorySelectionInputState runSpacing: constraints.isMobile ? 5.0 : -5.0, children: List.generate( widget.allValues.length, - (int idx) { + (idx) { final chipData = widget.allValues[idx]; - String label(chipData) { + String label(dynamic chipData) { if (chipData is CuisineFilter) { return cuisineReadable(chipData); } else if (chipData is DietaryRestrictionsFilter) { @@ -49,11 +49,11 @@ class _CategorySelectionInputState } return FilterChip( - color: MaterialStateColor.resolveWith((states) { - if (states.contains(MaterialState.hovered)) { + color: WidgetStateColor.resolveWith((states) { + if (states.contains(WidgetState.hovered)) { return MarketplaceTheme.secondary.withOpacity(.5); } - if (states.contains(MaterialState.selected)) { + if (states.contains(WidgetState.selected)) { return MarketplaceTheme.secondary.withOpacity(.3); } return Theme.of(context).splashColor; @@ -61,13 +61,13 @@ class _CategorySelectionInputState surfaceTintColor: Colors.transparent, shadowColor: Colors.transparent, backgroundColor: Colors.transparent, - padding: EdgeInsets.all(4), + padding: const EdgeInsets.all(4), label: Text( label(chipData), style: MarketplaceTheme.dossierParagraph, ), selected: widget.selectedValues.contains(chipData), - onSelected: (bool selected) { + onSelected: (selected) { setState( () { if (selected) { diff --git a/ai_recipe_generation/lib/widgets/highlight_border_on_hover_widget.dart b/ai_recipe_generation/lib/widgets/highlight_border_on_hover_widget.dart index 338a5de57..e9ab49635 100644 --- a/ai_recipe_generation/lib/widgets/highlight_border_on_hover_widget.dart +++ b/ai_recipe_generation/lib/widgets/highlight_border_on_hover_widget.dart @@ -1,4 +1,3 @@ -import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import '../theme.dart'; @@ -27,12 +26,12 @@ class _HighlightBorderOnHoverWidgetState @override Widget build(BuildContext context) { return MouseRegion( - onEnter: (PointerEnterEvent event) { + onEnter: (event) { setState(() { hovered = true; }); }, - onExit: (PointerExitEvent event) { + onExit: (event) { setState(() { hovered = false; }); diff --git a/ai_recipe_generation/lib/widgets/icon_loading_indicator.dart b/ai_recipe_generation/lib/widgets/icon_loading_indicator.dart index a33917388..489bbf375 100644 --- a/ai_recipe_generation/lib/widgets/icon_loading_indicator.dart +++ b/ai_recipe_generation/lib/widgets/icon_loading_indicator.dart @@ -79,8 +79,8 @@ class _IconLoadingAnimatorState extends State { ), ), child: AnimatedSwitcher( - duration: widget.animationDuration ?? Duration(milliseconds: 200), - transitionBuilder: (Widget child, Animation animation) { + duration: widget.animationDuration ?? const Duration(milliseconds: 200), + transitionBuilder: (child, animation) { return ScaleTransition( scale: animation, child: child, diff --git a/ai_recipe_generation/lib/widgets/marketplace_button_widget.dart b/ai_recipe_generation/lib/widgets/marketplace_button_widget.dart index 2f6c90043..142540be9 100644 --- a/ai_recipe_generation/lib/widgets/marketplace_button_widget.dart +++ b/ai_recipe_generation/lib/widgets/marketplace_button_widget.dart @@ -52,20 +52,20 @@ class _MarketplaceButtonState extends State { ), onPressed: widget.onPressed, style: ButtonStyle( - backgroundColor: MaterialStateColor.resolveWith((states) { - if (states.contains(MaterialState.hovered)) { + backgroundColor: WidgetStateColor.resolveWith((states) { + if (states.contains(WidgetState.hovered)) { return widget.hoverColor ?? MarketplaceTheme.secondary.withOpacity(.3); } return widget.buttonBackgroundColor ?? Theme.of(context).splashColor.withOpacity(.3); }), - shape: MaterialStateProperty.resolveWith( + shape: WidgetStateProperty.resolveWith( (states) { - if (states.contains(MaterialState.hovered)) { + if (states.contains(WidgetState.hovered)) { // TODO: how can I animate between states? } - return RoundedRectangleBorder( + return const RoundedRectangleBorder( side: BorderSide(color: Colors.black26), borderRadius: BorderRadius.all( Radius.circular(MarketplaceTheme.defaultBorderRadius), @@ -73,7 +73,7 @@ class _MarketplaceButtonState extends State { ); }, ), - textStyle: MaterialStateTextStyle.resolveWith( + textStyle: WidgetStateTextStyle.resolveWith( (states) { return MarketplaceTheme.dossierParagraph.copyWith( color: Colors.black45, diff --git a/ai_recipe_generation/lib/widgets/prompt_image_widget.dart b/ai_recipe_generation/lib/widgets/prompt_image_widget.dart index cd7cf8e19..417828fc9 100644 --- a/ai_recipe_generation/lib/widgets/prompt_image_widget.dart +++ b/ai_recipe_generation/lib/widgets/prompt_image_widget.dart @@ -8,7 +8,7 @@ import 'cross_image_widget.dart'; typedef OnTapRemoveImageCallback = void Function(XFile); class PromptImage extends StatelessWidget { - PromptImage({ + const PromptImage({ super.key, required this.file, this.onTapIcon, @@ -27,7 +27,7 @@ class PromptImage extends StatelessWidget { children: [ Positioned( child: ClipRRect( - borderRadius: BorderRadius.all( + borderRadius: const BorderRadius.all( Radius.circular( MarketplaceTheme.defaultBorderRadius, ), @@ -46,7 +46,7 @@ class PromptImage extends StatelessWidget { child: GestureDetector( onTap: onTapIcon, child: Container( - decoration: BoxDecoration( + decoration: const BoxDecoration( color: Colors.white, shape: BoxShape.circle, ), diff --git a/ai_recipe_generation/pubspec.yaml b/ai_recipe_generation/pubspec.yaml index 8c39d97a0..4ba6335df 100644 --- a/ai_recipe_generation/pubspec.yaml +++ b/ai_recipe_generation/pubspec.yaml @@ -31,6 +31,8 @@ dev_dependencies: flutter_test: sdk: flutter flutter_lints: ^3.0.1 + analysis_defaults: + path: ../analysis_defaults flutter: uses-material-design: true diff --git a/ai_recipe_generation/test/markdown_test.dart b/ai_recipe_generation/test/markdown_test.dart deleted file mode 100644 index 49b0df5fa..000000000 --- a/ai_recipe_generation/test/markdown_test.dart +++ /dev/null @@ -1,34 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; -import 'package:gemini_io_talk/util/markdown.dart'; - -const testMarkdown = ''' - ## Potato Leek Soup - -**Ingredients:** -- 3 leeks -- 3 potatoes -- 1 onion -- 2 cloves garlic -- 4 cups vegetable broth -- 1/2 cup milk -- 1/4 cup butter -- 1/4 cup flour -- 1 teaspoon salt -- 1/2 teaspoon pepper - -**Instructions:** -1. In a large pot or Dutch oven, heat the butter over medium heat. Add the leeks, onion, and garlic and cook until softened, about 5 minutes. -2. Add the potatoes, vegetable broth, salt, and pepper to the pot. Bring to a boil, then reduce heat and simmer for 15 minutes, or until the potatoes are tender. -3. In a small bowl, whisk together the milk and flour until smooth. Add to the pot and cook, stirring constantly, until the soup has thickened, about 5 minutes. -4. Serve immediately. -'''; - -main() { - group('parse recipe from markdown', () { - test('parses title', () { - final (gotTitle, title) = getTitleFromMarkdownRecipe(testMarkdown); - print(title); - expect(gotTitle, true); - }); - }); -}