"Fossies" - the Fresh Open Source Software Archive

Member "flutter-1.22.4/dev/integration_tests/flutter_gallery/lib/demo/pesto_demo.dart" (13 Nov 2020, 25688 Bytes) of package /linux/misc/flutter-1.22.4.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Dart source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 // Copyright 2014 The Flutter Authors. All rights reserved.
    2 // Use of this source code is governed by a BSD-style license that can be
    3 // found in the LICENSE file.
    4 
    5 import 'package:flutter/material.dart';
    6 import 'package:flutter/rendering.dart';
    7 
    8 class PestoDemo extends StatelessWidget {
    9   const PestoDemo({ Key key }) : super(key: key);
   10 
   11   static const String routeName = '/pesto';
   12 
   13   @override
   14   Widget build(BuildContext context) => PestoHome();
   15 }
   16 
   17 
   18 const String _kSmallLogoImage = 'logos/pesto/logo_small.png';
   19 const String _kGalleryAssetsPackage = 'flutter_gallery_assets';
   20 const double _kAppBarHeight = 128.0;
   21 const double _kFabHalfSize = 28.0; // TODO(mpcomplete): needs to adapt to screen size
   22 const double _kRecipePageMaxWidth = 500.0;
   23 
   24 final Set<Recipe> _favoriteRecipes = <Recipe>{};
   25 
   26 final ThemeData _kTheme = ThemeData(
   27   brightness: Brightness.light,
   28   primarySwatch: Colors.teal,
   29   accentColor: Colors.redAccent,
   30 );
   31 
   32 class PestoHome extends StatelessWidget {
   33   @override
   34   Widget build(BuildContext context) {
   35     return const RecipeGridPage(recipes: kPestoRecipes);
   36   }
   37 }
   38 
   39 class PestoFavorites extends StatelessWidget {
   40   @override
   41   Widget build(BuildContext context) {
   42     return RecipeGridPage(recipes: _favoriteRecipes.toList());
   43   }
   44 }
   45 
   46 class PestoStyle extends TextStyle {
   47   const PestoStyle({
   48     double fontSize = 12.0,
   49     FontWeight fontWeight,
   50     Color color = Colors.black87,
   51     double letterSpacing,
   52     double height,
   53   }) : super(
   54     inherit: false,
   55     color: color,
   56     fontFamily: 'Raleway',
   57     fontSize: fontSize,
   58     fontWeight: fontWeight,
   59     textBaseline: TextBaseline.alphabetic,
   60     letterSpacing: letterSpacing,
   61     height: height,
   62   );
   63 }
   64 
   65 // Displays a grid of recipe cards.
   66 class RecipeGridPage extends StatefulWidget {
   67   const RecipeGridPage({ Key key, this.recipes }) : super(key: key);
   68 
   69   final List<Recipe> recipes;
   70 
   71   @override
   72   _RecipeGridPageState createState() => _RecipeGridPageState();
   73 }
   74 
   75 class _RecipeGridPageState extends State<RecipeGridPage> {
   76   final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
   77 
   78   @override
   79   Widget build(BuildContext context) {
   80     final double statusBarHeight = MediaQuery.of(context).padding.top;
   81     return Theme(
   82       data: _kTheme.copyWith(platform: Theme.of(context).platform),
   83       child: Scaffold(
   84         key: scaffoldKey,
   85         floatingActionButton: FloatingActionButton(
   86           child: const Icon(Icons.edit),
   87           onPressed: () {
   88             scaffoldKey.currentState.showSnackBar(const SnackBar(
   89               content: Text('Not supported.'),
   90             ));
   91           },
   92         ),
   93         body: CustomScrollView(
   94           semanticChildCount: widget.recipes.length,
   95           slivers: <Widget>[
   96             _buildAppBar(context, statusBarHeight),
   97             _buildBody(context, statusBarHeight),
   98           ],
   99         ),
  100       ),
  101     );
  102   }
  103 
  104   Widget _buildAppBar(BuildContext context, double statusBarHeight) {
  105     return SliverAppBar(
  106       pinned: true,
  107       expandedHeight: _kAppBarHeight,
  108       actions: <Widget>[
  109         IconButton(
  110           icon: const Icon(Icons.search),
  111           tooltip: 'Search',
  112           onPressed: () {
  113             scaffoldKey.currentState.showSnackBar(const SnackBar(
  114               content: Text('Not supported.'),
  115             ));
  116           },
  117         ),
  118       ],
  119       flexibleSpace: LayoutBuilder(
  120         builder: (BuildContext context, BoxConstraints constraints) {
  121           final Size size = constraints.biggest;
  122           final double appBarHeight = size.height - statusBarHeight;
  123           final double t = (appBarHeight - kToolbarHeight) / (_kAppBarHeight - kToolbarHeight);
  124           final double extraPadding = Tween<double>(begin: 10.0, end: 24.0).transform(t);
  125           final double logoHeight = appBarHeight - 1.5 * extraPadding;
  126           return Padding(
  127             padding: EdgeInsets.only(
  128               top: statusBarHeight + 0.5 * extraPadding,
  129               bottom: extraPadding,
  130             ),
  131             child: Center(
  132               child: PestoLogo(height: logoHeight, t: t.clamp(0.0, 1.0) as double),
  133             ),
  134           );
  135         },
  136       ),
  137     );
  138   }
  139 
  140   Widget _buildBody(BuildContext context, double statusBarHeight) {
  141     final EdgeInsets mediaPadding = MediaQuery.of(context).padding;
  142     final EdgeInsets padding = EdgeInsets.only(
  143       top: 8.0,
  144       left: 8.0 + mediaPadding.left,
  145       right: 8.0 + mediaPadding.right,
  146       bottom: 8.0,
  147     );
  148     return SliverPadding(
  149       padding: padding,
  150       sliver: SliverGrid(
  151         gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
  152           maxCrossAxisExtent: _kRecipePageMaxWidth,
  153           crossAxisSpacing: 8.0,
  154           mainAxisSpacing: 8.0,
  155         ),
  156         delegate: SliverChildBuilderDelegate(
  157           (BuildContext context, int index) {
  158             final Recipe recipe = widget.recipes[index];
  159             return RecipeCard(
  160               recipe: recipe,
  161               onTap: () { showRecipePage(context, recipe); },
  162             );
  163           },
  164           childCount: widget.recipes.length,
  165         ),
  166       ),
  167     );
  168   }
  169 
  170   void showFavoritesPage(BuildContext context) {
  171     Navigator.push(context, MaterialPageRoute<void>(
  172       settings: const RouteSettings(name: '/pesto/favorites'),
  173       builder: (BuildContext context) => PestoFavorites(),
  174     ));
  175   }
  176 
  177   void showRecipePage(BuildContext context, Recipe recipe) {
  178     Navigator.push(context, MaterialPageRoute<void>(
  179       settings: const RouteSettings(name: '/pesto/recipe'),
  180       builder: (BuildContext context) {
  181         return Theme(
  182           data: _kTheme.copyWith(platform: Theme.of(context).platform),
  183           child: RecipePage(recipe: recipe),
  184         );
  185       },
  186     ));
  187   }
  188 }
  189 
  190 class PestoLogo extends StatefulWidget {
  191   const PestoLogo({this.height, this.t});
  192 
  193   final double height;
  194   final double t;
  195 
  196   @override
  197   _PestoLogoState createState() => _PestoLogoState();
  198 }
  199 
  200 class _PestoLogoState extends State<PestoLogo> {
  201   // Native sizes for logo and its image/text components.
  202   static const double kLogoHeight = 162.0;
  203   static const double kLogoWidth = 220.0;
  204   static const double kImageHeight = 108.0;
  205   static const double kTextHeight = 48.0;
  206   final TextStyle titleStyle = const PestoStyle(fontSize: kTextHeight, fontWeight: FontWeight.w900, color: Colors.white, letterSpacing: 3.0);
  207   final RectTween _textRectTween = RectTween(
  208     begin: const Rect.fromLTWH(0.0, kLogoHeight, kLogoWidth, kTextHeight),
  209     end: const Rect.fromLTWH(0.0, kImageHeight, kLogoWidth, kTextHeight),
  210   );
  211   final Curve _textOpacity = const Interval(0.4, 1.0, curve: Curves.easeInOut);
  212   final RectTween _imageRectTween = RectTween(
  213     begin: const Rect.fromLTWH(0.0, 0.0, kLogoWidth, kLogoHeight),
  214     end: const Rect.fromLTWH(0.0, 0.0, kLogoWidth, kImageHeight),
  215   );
  216 
  217   @override
  218   Widget build(BuildContext context) {
  219     return Semantics(
  220       namesRoute: true,
  221       child: Transform(
  222         transform: Matrix4.identity()..scale(widget.height / kLogoHeight),
  223         alignment: Alignment.topCenter,
  224         child: SizedBox(
  225           width: kLogoWidth,
  226           child: Stack(
  227             clipBehavior: Clip.none,
  228             children: <Widget>[
  229               Positioned.fromRect(
  230                 rect: _imageRectTween.lerp(widget.t),
  231                 child: Image.asset(
  232                   _kSmallLogoImage,
  233                   package: _kGalleryAssetsPackage,
  234                   fit: BoxFit.contain,
  235                 ),
  236               ),
  237               Positioned.fromRect(
  238                 rect: _textRectTween.lerp(widget.t),
  239                 child: Opacity(
  240                   opacity: _textOpacity.transform(widget.t),
  241                   child: Text('PESTO', style: titleStyle, textAlign: TextAlign.center),
  242                 ),
  243               ),
  244             ],
  245           ),
  246         ),
  247       ),
  248     );
  249   }
  250 }
  251 
  252 // A card with the recipe's image, author, and title.
  253 class RecipeCard extends StatelessWidget {
  254   const RecipeCard({ Key key, this.recipe, this.onTap }) : super(key: key);
  255 
  256   final Recipe recipe;
  257   final VoidCallback onTap;
  258 
  259   TextStyle get titleStyle => const PestoStyle(fontSize: 24.0, fontWeight: FontWeight.w600);
  260   TextStyle get authorStyle => const PestoStyle(fontWeight: FontWeight.w500, color: Colors.black54);
  261 
  262   @override
  263   Widget build(BuildContext context) {
  264     return GestureDetector(
  265       onTap: onTap,
  266       child: Card(
  267         child: Column(
  268           crossAxisAlignment: CrossAxisAlignment.start,
  269           children: <Widget>[
  270             Hero(
  271               tag: 'packages/$_kGalleryAssetsPackage/${recipe.imagePath}',
  272               child: AspectRatio(
  273                 aspectRatio: 4.0 / 3.0,
  274                 child: Image.asset(
  275                   recipe.imagePath,
  276                   package: recipe.imagePackage,
  277                   fit: BoxFit.cover,
  278                   semanticLabel: recipe.name,
  279                 ),
  280               ),
  281             ),
  282             Expanded(
  283               child: Row(
  284                 children: <Widget>[
  285                   Padding(
  286                     padding: const EdgeInsets.all(16.0),
  287                     child: Image.asset(
  288                       recipe.ingredientsImagePath,
  289                       package: recipe.ingredientsImagePackage,
  290                       width: 48.0,
  291                       height: 48.0,
  292                     ),
  293                   ),
  294                   Expanded(
  295                     child: Column(
  296                       crossAxisAlignment: CrossAxisAlignment.start,
  297                       mainAxisAlignment: MainAxisAlignment.center,
  298                       children: <Widget>[
  299                         Text(recipe.name, style: titleStyle, softWrap: false, overflow: TextOverflow.ellipsis),
  300                         Text(recipe.author, style: authorStyle),
  301                       ],
  302                     ),
  303                   ),
  304                 ],
  305               ),
  306             ),
  307           ],
  308         ),
  309       ),
  310     );
  311   }
  312 }
  313 
  314 // Displays one recipe. Includes the recipe sheet with a background image.
  315 class RecipePage extends StatefulWidget {
  316   const RecipePage({ Key key, this.recipe }) : super(key: key);
  317 
  318   final Recipe recipe;
  319 
  320   @override
  321   _RecipePageState createState() => _RecipePageState();
  322 }
  323 
  324 class _RecipePageState extends State<RecipePage> {
  325   final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
  326   final TextStyle menuItemStyle = const PestoStyle(fontSize: 15.0, color: Colors.black54, height: 24.0/15.0);
  327 
  328   double _getAppBarHeight(BuildContext context) => MediaQuery.of(context).size.height * 0.3;
  329 
  330   @override
  331   Widget build(BuildContext context) {
  332     // The full page content with the recipe's image behind it. This
  333     // adjusts based on the size of the screen. If the recipe sheet touches
  334     // the edge of the screen, use a slightly different layout.
  335     final double appBarHeight = _getAppBarHeight(context);
  336     final Size screenSize = MediaQuery.of(context).size;
  337     final bool fullWidth = screenSize.width < _kRecipePageMaxWidth;
  338     final bool isFavorite = _favoriteRecipes.contains(widget.recipe);
  339     return Scaffold(
  340       key: _scaffoldKey,
  341       body: Stack(
  342         children: <Widget>[
  343           Positioned(
  344             top: 0.0,
  345             left: 0.0,
  346             right: 0.0,
  347             height: appBarHeight + _kFabHalfSize,
  348             child: Hero(
  349               tag: 'packages/$_kGalleryAssetsPackage/${widget.recipe.imagePath}',
  350               child: Image.asset(
  351                 widget.recipe.imagePath,
  352                 package: widget.recipe.imagePackage,
  353                 fit: fullWidth ? BoxFit.fitWidth : BoxFit.cover,
  354               ),
  355             ),
  356           ),
  357           CustomScrollView(
  358             slivers: <Widget>[
  359               SliverAppBar(
  360                 expandedHeight: appBarHeight - _kFabHalfSize,
  361                 backgroundColor: Colors.transparent,
  362                 actions: <Widget>[
  363                   PopupMenuButton<String>(
  364                     onSelected: (String item) { },
  365                     itemBuilder: (BuildContext context) => <PopupMenuItem<String>>[
  366                       _buildMenuItem(Icons.share, 'Tweet recipe'),
  367                       _buildMenuItem(Icons.email, 'Email recipe'),
  368                       _buildMenuItem(Icons.message, 'Message recipe'),
  369                       _buildMenuItem(Icons.people, 'Share on Facebook'),
  370                     ],
  371                   ),
  372                 ],
  373                 flexibleSpace: const FlexibleSpaceBar(
  374                   background: DecoratedBox(
  375                     decoration: BoxDecoration(
  376                       gradient: LinearGradient(
  377                         begin: Alignment(0.0, -1.0),
  378                         end: Alignment(0.0, -0.2),
  379                         colors: <Color>[Color(0x60000000), Color(0x00000000)],
  380                       ),
  381                     ),
  382                   ),
  383                 ),
  384               ),
  385               SliverToBoxAdapter(
  386                 child: Stack(
  387                   children: <Widget>[
  388                     Container(
  389                       padding: const EdgeInsets.only(top: _kFabHalfSize),
  390                       width: fullWidth ? null : _kRecipePageMaxWidth,
  391                       child: RecipeSheet(recipe: widget.recipe),
  392                     ),
  393                     Positioned(
  394                       right: 16.0,
  395                       child: FloatingActionButton(
  396                         child: Icon(isFavorite ? Icons.favorite : Icons.favorite_border),
  397                         onPressed: _toggleFavorite,
  398                       ),
  399                     ),
  400                   ],
  401                 ),
  402               ),
  403             ],
  404           ),
  405         ],
  406       ),
  407     );
  408   }
  409 
  410   PopupMenuItem<String> _buildMenuItem(IconData icon, String label) {
  411     return PopupMenuItem<String>(
  412       child: Row(
  413         children: <Widget>[
  414           Padding(
  415             padding: const EdgeInsets.only(right: 24.0),
  416             child: Icon(icon, color: Colors.black54),
  417           ),
  418           Text(label, style: menuItemStyle),
  419         ],
  420       ),
  421     );
  422   }
  423 
  424   void _toggleFavorite() {
  425     setState(() {
  426       if (_favoriteRecipes.contains(widget.recipe))
  427         _favoriteRecipes.remove(widget.recipe);
  428       else
  429         _favoriteRecipes.add(widget.recipe);
  430     });
  431   }
  432 }
  433 
  434 /// Displays the recipe's name and instructions.
  435 class RecipeSheet extends StatelessWidget {
  436   RecipeSheet({ Key key, this.recipe }) : super(key: key);
  437 
  438   final TextStyle titleStyle = const PestoStyle(fontSize: 34.0);
  439   final TextStyle descriptionStyle = const PestoStyle(fontSize: 15.0, color: Colors.black54, height: 24.0/15.0);
  440   final TextStyle itemStyle = const PestoStyle(fontSize: 15.0, height: 24.0/15.0);
  441   final TextStyle itemAmountStyle = PestoStyle(fontSize: 15.0, color: _kTheme.primaryColor, height: 24.0/15.0);
  442   final TextStyle headingStyle = const PestoStyle(fontSize: 16.0, fontWeight: FontWeight.bold, height: 24.0/15.0);
  443 
  444   final Recipe recipe;
  445 
  446   @override
  447   Widget build(BuildContext context) {
  448     return Material(
  449       child: SafeArea(
  450         top: false,
  451         bottom: false,
  452         child: Padding(
  453           padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 40.0),
  454           child: Table(
  455             columnWidths: const <int, TableColumnWidth>{
  456               0: FixedColumnWidth(64.0),
  457             },
  458             children: <TableRow>[
  459               TableRow(
  460                 children: <Widget>[
  461                   TableCell(
  462                     verticalAlignment: TableCellVerticalAlignment.middle,
  463                     child: Image.asset(
  464                       recipe.ingredientsImagePath,
  465                       package: recipe.ingredientsImagePackage,
  466                       width: 32.0,
  467                       height: 32.0,
  468                       alignment: Alignment.centerLeft,
  469                       fit: BoxFit.scaleDown,
  470                     ),
  471                   ),
  472                   TableCell(
  473                     verticalAlignment: TableCellVerticalAlignment.middle,
  474                     child: Text(recipe.name, style: titleStyle),
  475                   ),
  476                 ]
  477               ),
  478               TableRow(
  479                 children: <Widget>[
  480                   const SizedBox(),
  481                   Padding(
  482                     padding: const EdgeInsets.only(top: 8.0, bottom: 4.0),
  483                     child: Text(recipe.description, style: descriptionStyle),
  484                   ),
  485                 ]
  486               ),
  487               TableRow(
  488                 children: <Widget>[
  489                   const SizedBox(),
  490                   Padding(
  491                     padding: const EdgeInsets.only(top: 24.0, bottom: 4.0),
  492                     child: Text('Ingredients', style: headingStyle),
  493                   ),
  494                 ]
  495               ),
  496               ...recipe.ingredients.map<TableRow>((RecipeIngredient ingredient) {
  497                 return _buildItemRow(ingredient.amount, ingredient.description);
  498               }),
  499               TableRow(
  500                 children: <Widget>[
  501                   const SizedBox(),
  502                   Padding(
  503                     padding: const EdgeInsets.only(top: 24.0, bottom: 4.0),
  504                     child: Text('Steps', style: headingStyle),
  505                   ),
  506                 ]
  507               ),
  508               ...recipe.steps.map<TableRow>((RecipeStep step) {
  509                 return _buildItemRow(step.duration ?? '', step.description);
  510               }),
  511             ],
  512           ),
  513         ),
  514       ),
  515     );
  516   }
  517 
  518   TableRow _buildItemRow(String left, String right) {
  519     return TableRow(
  520       children: <Widget>[
  521         Padding(
  522           padding: const EdgeInsets.symmetric(vertical: 4.0),
  523           child: Text(left, style: itemAmountStyle),
  524         ),
  525         Padding(
  526           padding: const EdgeInsets.symmetric(vertical: 4.0),
  527           child: Text(right, style: itemStyle),
  528         ),
  529       ],
  530     );
  531   }
  532 }
  533 
  534 class Recipe {
  535   const Recipe({
  536     this.name,
  537     this.author,
  538     this.description,
  539     this.imagePath,
  540     this.imagePackage,
  541     this.ingredientsImagePath,
  542     this.ingredientsImagePackage,
  543     this.ingredients,
  544     this.steps,
  545   });
  546 
  547   final String name;
  548   final String author;
  549   final String description;
  550   final String imagePath;
  551   final String imagePackage;
  552   final String ingredientsImagePath;
  553   final String ingredientsImagePackage;
  554   final List<RecipeIngredient> ingredients;
  555   final List<RecipeStep> steps;
  556 }
  557 
  558 class RecipeIngredient {
  559   const RecipeIngredient({this.amount, this.description});
  560 
  561   final String amount;
  562   final String description;
  563 }
  564 
  565 class RecipeStep {
  566   const RecipeStep({this.duration, this.description});
  567 
  568   final String duration;
  569   final String description;
  570 }
  571 
  572 const List<Recipe> kPestoRecipes = <Recipe>[
  573   Recipe(
  574     name: 'Roasted Chicken',
  575     author: 'Peter Carlsson',
  576     ingredientsImagePath: 'food/icons/main.png',
  577     ingredientsImagePackage: _kGalleryAssetsPackage,
  578     description: 'The perfect dish to welcome your family and friends with on a crisp autumn night. Pair with roasted veggies to truly impress them.',
  579     imagePath: 'food/roasted_chicken.png',
  580     imagePackage: _kGalleryAssetsPackage,
  581     ingredients: <RecipeIngredient>[
  582       RecipeIngredient(amount: '1 whole', description: 'Chicken'),
  583       RecipeIngredient(amount: '1/2 cup', description: 'Butter'),
  584       RecipeIngredient(amount: '1 tbsp', description: 'Onion powder'),
  585       RecipeIngredient(amount: '1 tbsp', description: 'Freshly ground pepper'),
  586       RecipeIngredient(amount: '1 tsp', description: 'Salt'),
  587     ],
  588     steps: <RecipeStep>[
  589       RecipeStep(duration: '1 min', description: 'Put in oven'),
  590       RecipeStep(duration: '1hr 45 min', description: 'Cook'),
  591     ],
  592   ),
  593   Recipe(
  594     name: 'Chopped Beet Leaves',
  595     author: 'Trevor Hansen',
  596     ingredientsImagePath: 'food/icons/veggie.png',
  597     ingredientsImagePackage: _kGalleryAssetsPackage,
  598     description: 'This vegetable has more to offer than just its root. Beet greens can be tossed into a salad to add some variety or sauteed on its own with some oil and garlic.',
  599     imagePath: 'food/chopped_beet_leaves.png',
  600     imagePackage: _kGalleryAssetsPackage,
  601     ingredients: <RecipeIngredient>[
  602        RecipeIngredient(amount: '3 cups', description: 'Beet greens'),
  603     ],
  604     steps: <RecipeStep>[
  605       RecipeStep(duration: '5 min', description: 'Chop'),
  606     ],
  607   ),
  608   Recipe(
  609     name: 'Pesto Pasta',
  610     author: 'Ali Connors',
  611     ingredientsImagePath: 'food/icons/main.png',
  612     ingredientsImagePackage: _kGalleryAssetsPackage,
  613     description: "With this pesto recipe, you can quickly whip up a meal to satisfy your savory needs. And if you're feeling festive, you can add bacon to taste.",
  614     imagePath: 'food/pesto_pasta.png',
  615     imagePackage: _kGalleryAssetsPackage,
  616     ingredients: <RecipeIngredient>[
  617       RecipeIngredient(amount: '1/4 cup ', description: 'Pasta'),
  618       RecipeIngredient(amount: '2 cups', description: 'Fresh basil leaves'),
  619       RecipeIngredient(amount: '1/2 cup', description: 'Parmesan cheese'),
  620       RecipeIngredient(amount: '1/2 cup', description: 'Extra virgin olive oil'),
  621       RecipeIngredient(amount: '1/3 cup', description: 'Pine nuts'),
  622       RecipeIngredient(amount: '1/4 cup', description: 'Lemon juice'),
  623       RecipeIngredient(amount: '3 cloves', description: 'Garlic'),
  624       RecipeIngredient(amount: '1/4 tsp', description: 'Salt'),
  625       RecipeIngredient(amount: '1/8 tsp', description: 'Pepper'),
  626       RecipeIngredient(amount: '3 lbs', description: 'Bacon'),
  627     ],
  628     steps: <RecipeStep>[
  629       RecipeStep(duration: '15 min', description: 'Blend'),
  630     ],
  631   ),
  632   Recipe(
  633     name: 'Cherry Pie',
  634     author: 'Sandra Adams',
  635     ingredientsImagePath: 'food/icons/main.png',
  636     ingredientsImagePackage: _kGalleryAssetsPackage,
  637     description: "Sometimes when you're craving some cheer in your life you can jumpstart your day with some cherry pie. Dessert for breakfast is perfectly acceptable.",
  638     imagePath: 'food/cherry_pie.png',
  639     imagePackage: _kGalleryAssetsPackage,
  640     ingredients: <RecipeIngredient>[
  641       RecipeIngredient(amount: '1', description: 'Pie crust'),
  642       RecipeIngredient(amount: '4 cups', description: 'Fresh or frozen cherries'),
  643       RecipeIngredient(amount: '1 cup', description: 'Granulated sugar'),
  644       RecipeIngredient(amount: '4 tbsp', description: 'Cornstarch'),
  645       RecipeIngredient(amount: '1½ tbsp', description: 'Butter'),
  646     ],
  647     steps: <RecipeStep>[
  648       RecipeStep(duration: '15 min', description: 'Mix'),
  649       RecipeStep(duration: '1hr 30 min', description: 'Bake'),
  650     ],
  651   ),
  652   Recipe(
  653     name: 'Spinach Salad',
  654     author: 'Peter Carlsson',
  655     ingredientsImagePath: 'food/icons/spicy.png',
  656     ingredientsImagePackage: _kGalleryAssetsPackage,
  657     description: "Everyone's favorite leafy green is back. Paired with fresh sliced onion, it's ready to tackle any dish, whether it be a salad or an egg scramble.",
  658     imagePath: 'food/spinach_onion_salad.png',
  659     imagePackage: _kGalleryAssetsPackage,
  660     ingredients: <RecipeIngredient>[
  661       RecipeIngredient(amount: '4 cups', description: 'Spinach'),
  662       RecipeIngredient(amount: '1 cup', description: 'Sliced onion'),
  663     ],
  664     steps: <RecipeStep>[
  665       RecipeStep(duration: '5 min', description: 'Mix'),
  666     ],
  667   ),
  668   Recipe(
  669     name: 'Butternut Squash Soup',
  670     author: 'Ali Connors',
  671     ingredientsImagePath: 'food/icons/healthy.png',
  672     ingredientsImagePackage: _kGalleryAssetsPackage,
  673     description: 'This creamy butternut squash soup will warm you on the chilliest of winter nights and bring a delightful pop of orange to the dinner table.',
  674     imagePath: 'food/butternut_squash_soup.png',
  675     imagePackage: _kGalleryAssetsPackage,
  676     ingredients: <RecipeIngredient>[
  677       RecipeIngredient(amount: '1', description: 'Butternut squash'),
  678       RecipeIngredient(amount: '4 cups', description: 'Chicken stock'),
  679       RecipeIngredient(amount: '2', description: 'Potatoes'),
  680       RecipeIngredient(amount: '1', description: 'Onion'),
  681       RecipeIngredient(amount: '1', description: 'Carrot'),
  682       RecipeIngredient(amount: '1', description: 'Celery'),
  683       RecipeIngredient(amount: '1 tsp', description: 'Salt'),
  684       RecipeIngredient(amount: '1 tsp', description: 'Pepper'),
  685     ],
  686     steps: <RecipeStep>[
  687       RecipeStep(duration: '10 min', description: 'Prep vegetables'),
  688       RecipeStep(duration: '5 min', description: 'Stir'),
  689       RecipeStep(duration: '1 hr 10 min', description: 'Cook'),
  690     ],
  691   ),
  692   Recipe(
  693     name: 'Spanakopita',
  694     author: 'Trevor Hansen',
  695     ingredientsImagePath: 'food/icons/quick.png',
  696     ingredientsImagePackage: _kGalleryAssetsPackage,
  697     description: "You 'feta' believe this is a crowd-pleaser! Flaky phyllo pastry surrounds a delicious mixture of spinach and cheeses to create the perfect appetizer.",
  698     imagePath: 'food/spanakopita.png',
  699     imagePackage: _kGalleryAssetsPackage,
  700     ingredients: <RecipeIngredient>[
  701       RecipeIngredient(amount: '1 lb', description: 'Spinach'),
  702       RecipeIngredient(amount: '½ cup', description: 'Feta cheese'),
  703       RecipeIngredient(amount: '½ cup', description: 'Cottage cheese'),
  704       RecipeIngredient(amount: '2', description: 'Eggs'),
  705       RecipeIngredient(amount: '1', description: 'Onion'),
  706       RecipeIngredient(amount: '½ lb', description: 'Phyllo dough'),
  707     ],
  708     steps: <RecipeStep>[
  709       RecipeStep(duration: '5 min', description: 'Sauté vegetables'),
  710       RecipeStep(duration: '3 min', description: 'Stir vegetables and other filling ingredients'),
  711       RecipeStep(duration: '10 min', description: 'Fill phyllo squares half-full with filling and fold.'),
  712       RecipeStep(duration: '40 min', description: 'Bake'),
  713     ],
  714   ),
  715 ];