diff --git a/.azure/pipeline.yaml b/.azure/pipeline.yaml index 2cec3d1..e8f9a1c 100644 --- a/.azure/pipeline.yaml +++ b/.azure/pipeline.yaml @@ -60,7 +60,7 @@ stages: workingDirectory: 'widgetbook' targetType: 'inline' script: | - $HOME/.pub-cache/bin/widgetbook publish \ + $HOME/.pub-cache/bin/widgetbook cloud build push \ --api-key 7396fbb0851ab29f0cc78246355b786de54a087dba3828acde90ac7afef7d97f \ --branch '$(Build.SourceBranch)' \ --repository '$(Build.DefinitionName)' \ diff --git a/codemagic.yaml b/codemagic.yaml index c950554..7157d8b 100644 --- a/codemagic.yaml +++ b/codemagic.yaml @@ -5,9 +5,6 @@ workflows: triggering: events: - push - branch_patterns: - - pattern: main - include: true environment: flutter: stable @@ -33,41 +30,6 @@ workflows: - name: Upload Widgetbook working_directory: widgetbook script: | - widgetbook publish \ + widgetbook cloud build push \ --api-key $WIDGETBOOK_API_KEY \ - --branch $CM_BRANCH - - widgetbook-review: - name: Upload Widgetbook Review - - triggering: - events: - - pull_request - - environment: - flutter: stable - groups: - - Widgetbook Credentials - - scripts: - - name: Build App - script: | - flutter pub get - flutter gen-l10n - - - name: Install Widgetbook CLI - script: dart pub global activate widgetbook_cli - - - name: Build Widgetbook - working_directory: widgetbook - script: | - flutter pub get - dart run build_runner build -d - flutter build web -t lib/main.dart - - - name: Upload Widgetbook - working_directory: widgetbook - script: | - widgetbook publish \ - --api-key $WIDGETBOOK_API_KEY \ - --base-branch source/$CM_PULL_REQUEST_DEST + --branch $CM_BRANCH \ No newline at end of file diff --git a/lib/about/about_screen.dart b/lib/about/about_screen.dart index c519971..dafe480 100644 --- a/lib/about/about_screen.dart +++ b/lib/about/about_screen.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -import '../theme/app_theme.dart'; +import '../ui/ui.dart'; class AboutScreen extends StatelessWidget { const AboutScreen({super.key}); @@ -18,11 +18,11 @@ class AboutScreen extends StatelessWidget { height: 100, ), SizedBox( - height: AppTheme.of(context).spacing.medium, + height: AppTheme.of(context).spacing.m, ), Text( 'Created by Widgetbook', - style: AppTheme.of(context).typography.labelMedium14, + style: AppTheme.of(context).typography.label, ), ], ), diff --git a/lib/app.dart b/lib/app.dart index 7e013b3..0856305 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -3,27 +3,12 @@ import 'package:flutter/widgets.dart'; import 'package:go_router/go_router.dart'; import 'about/about_screen.dart'; -import 'basket/basket.dart'; -import 'home/home.dart'; +import 'features/basket/basket.dart'; import 'l10n/app_localizations.dart'; -import 'repositories/fruits_repository.dart'; -import 'theme/theme.dart'; +import 'ui/ui.dart'; final _router = GoRouter( routes: [ - GoRoute( - path: '/', - builder: (_, __) => FutureBuilder( - future: FruitRepository().getFruits(), - builder: (_, snapshot) { - if (!snapshot.hasData) return const Placeholder(); - - return HomeScreen( - fruits: snapshot.data!, - ); - }, - ), - ), GoRoute( path: '/about', builder: (context, state) => const AboutScreen(), @@ -33,7 +18,7 @@ final _router = GoRouter( builder: (context, state) { final basketState = BasketState.of(context); - return BasketScreen( + return BasketView( basket: basketState.store, delivery: basketState.delivery, subTotal: basketState.subTotal, @@ -50,18 +35,18 @@ class App extends StatelessWidget { Widget build(BuildContext context) { return AppTheme( data: MediaQuery.of(context).platformBrightness == Brightness.light - ? lightTheme - : darkTheme, + ? AppThemeData.light + : AppThemeData.dark, child: Builder( builder: (context) { return BasketScope( child: ColoredBox( - color: AppTheme.of(context).surface.primary, + color: AppTheme.of(context).background.primary, child: SafeArea( child: WidgetsApp.router( title: 'Grocery App', debugShowCheckedModeBanner: false, - color: lightTheme.surface.primary, + color: AppTheme.of(context).background.primary, routerConfig: _router, supportedLocales: AppLocalizations.supportedLocales, localizationsDelegates: diff --git a/lib/basket/basket.dart b/lib/basket/basket.dart deleted file mode 100644 index f7a0f2c..0000000 --- a/lib/basket/basket.dart +++ /dev/null @@ -1,4 +0,0 @@ -export 'basket_screen.dart'; -export 'state/basket_scope.dart'; -export 'state/basket_state.dart'; -export 'widgets/widgets.dart'; diff --git a/lib/basket/basket_screen.dart b/lib/basket/basket_screen.dart deleted file mode 100644 index c9abf2f..0000000 --- a/lib/basket/basket_screen.dart +++ /dev/null @@ -1,123 +0,0 @@ -import 'dart:math'; - -import 'package:flutter/widgets.dart'; - -import '../core/app_bar.dart'; -import '../core/primary_button.dart'; -import '../l10n/app_localizations.dart'; -import '../repositories/fruit.dart'; -import '../theme/app_theme.dart'; -import 'state/basket_state.dart'; -import 'widgets/widgets.dart'; - -class BasketScreen extends StatelessWidget { - const BasketScreen({ - super.key, - required this.basket, - required this.delivery, - required this.subTotal, - }); - - final Map basket; - final double delivery; - final double subTotal; - - Widget _buildEmptyPage(BuildContext context) { - return Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Expanded(child: EmptyBasketPlaceholder()), - PrimaryButton( - content: AppLocalizations.of(context)!.startShopping, - ), - ], - ); - } - - Widget _buildFilledPage(BuildContext context) { - return Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - AppLocalizations.of(context)!.basketHeadline, - style: AppTheme.of(context).typography.displayRegular32, - ), - SizedBox( - height: AppTheme.of(context).spacing.large, - ), - Expanded( - child: LayoutBuilder( - builder: (context, constraint) { - final columns = max( - 1, - constraint.maxWidth ~/ 350, - ); - return GridView.count( - key: ValueKey(basket), - crossAxisCount: columns, - padding: EdgeInsets.zero, - mainAxisSpacing: AppTheme.of(context).spacing.medium, - crossAxisSpacing: AppTheme.of(context).spacing.medium, - // TODO this is a bit of a hack - // 116 is the height of the BasketCard - childAspectRatio: constraint.maxWidth / columns / 116, - children: [ - for (final fruit in basket.keys) - BasketCard( - fruit: fruit, - count: basket[fruit]!.quantity, - onFruitAdded: () => - BasketState.of(context).addFruit(fruit), - onFruitRemoved: () => - BasketState.of(context).removeFruit(fruit), - ), - ], - ); - }, - ), - ), - Summary( - delivery: delivery, - subTotal: subTotal, - ), - SizedBox( - height: AppTheme.of(context).spacing.large, - ), - PrimaryButton( - content: AppLocalizations.of(context)!.basketContinueToShipping, - ), - ], - ); - } - - @override - Widget build(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - AppBar( - title: 'Groceries App', - basketSize: basket.length, - ), - SizedBox( - height: AppTheme.of(context).spacing.large, - ), - Expanded( - child: Padding( - padding: EdgeInsets.symmetric( - horizontal: AppTheme.of(context).spacing.medium, - ), - child: basket.isEmpty - ? _buildEmptyPage(context) - : _buildFilledPage(context), - ), - ), - SizedBox( - height: AppTheme.of(context).spacing.medium, - ), - ], - ); - } -} diff --git a/lib/basket/widgets/basket_card.dart b/lib/basket/widgets/basket_card.dart deleted file mode 100644 index bd9dda3..0000000 --- a/lib/basket/widgets/basket_card.dart +++ /dev/null @@ -1,103 +0,0 @@ -import 'package:flutter/material.dart'; - -import '../../core/core.dart' as core; -import '../../core/quantity_button.dart'; -import '../../l10n/app_localizations.dart'; -import '../../repositories/fruit.dart'; -import '../../theme/theme.dart'; -import 'quantity.dart'; - -class BasketCard extends StatelessWidget { - const BasketCard({ - super.key, - required this.fruit, - required this.count, - this.onFruitAdded, - this.onFruitRemoved, - }); - - final Fruit fruit; - final int count; - final VoidCallback? onFruitAdded; - final VoidCallback? onFruitRemoved; - - double get total => fruit.price * count; - - @override - Widget build(BuildContext context) { - return core.Card( - child: Padding( - padding: EdgeInsets.all( - AppTheme.of(context).spacing.small, - ), - child: Row( - children: [ - Container( - height: 100, - width: 100, - decoration: BoxDecoration( - borderRadius: BorderRadius.all( - Radius.circular( - AppTheme.of(context).radius.extraSmall, - ), - ), - image: DecorationImage( - fit: BoxFit.cover, - image: NetworkImage(fruit.imageUrl), - ), - ), - ), - SizedBox( - width: AppTheme.of(context).spacing.medium, - ), - Expanded( - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - fruit.name, - style: AppTheme.of(context).typography.headingSemibold20, - ), - SizedBox( - height: AppTheme.of(context).spacing.extraSmall - 2, - ), - Text( - '\$${fruit.price}/${AppLocalizations.of(context)!.unit}', - style: AppTheme.of(context).typography.bodyRegular12, - ), - SizedBox( - height: AppTheme.of(context).spacing.medium, - ), - Row( - children: [ - QuantityButton.remove( - onPressed: onFruitRemoved, - ), - Padding( - padding: EdgeInsets.symmetric( - horizontal: AppTheme.of(context).spacing.extraSmall, - ), - child: Quantity( - value: count, - ), - ), - QuantityButton.add( - onPressed: onFruitAdded, - ), - const Spacer(), - Text( - '\$${total.toStringAsFixed(2)}', - style: AppTheme.of(context).typography.bodySemiBold16, - ), - ], - ), - ], - ), - ), - ], - ), - ), - ); - } -} diff --git a/lib/basket/widgets/empty_basket_placeholder.dart b/lib/basket/widgets/empty_basket_placeholder.dart deleted file mode 100644 index 1c81845..0000000 --- a/lib/basket/widgets/empty_basket_placeholder.dart +++ /dev/null @@ -1,54 +0,0 @@ -import 'package:flutter/material.dart'; - -import '../../l10n/app_localizations.dart'; -import '../../theme/app_theme.dart'; - -class EmptyBasketPlaceholder extends StatelessWidget { - const EmptyBasketPlaceholder({super.key}); - - @override - Widget build(BuildContext context) { - return Center( - child: Container( - constraints: const BoxConstraints( - maxWidth: 320, - ), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Container( - height: 102, - width: 102, - decoration: BoxDecoration( - color: AppTheme.of(context).surface.light, - borderRadius: BorderRadius.circular( - AppTheme.of(context).radius.full, - ), - ), - child: Icon( - Icons.local_mall_outlined, - size: 56, - color: AppTheme.of(context).surface.secondary, - ), - ), - SizedBox( - height: AppTheme.of(context).spacing.medium, - ), - Text( - AppLocalizations.of(context)!.basketEmpty, - style: AppTheme.of(context).typography.headingMedium24, - ), - SizedBox( - height: AppTheme.of(context).spacing.small, - ), - Text( - AppLocalizations.of(context)!.basketEmptyMessage, - textAlign: TextAlign.center, - style: AppTheme.of(context).typography.bodyRegular14, - ), - ], - ), - ), - ); - } -} diff --git a/lib/basket/widgets/quantity.dart b/lib/basket/widgets/quantity.dart deleted file mode 100644 index 60a60aa..0000000 --- a/lib/basket/widgets/quantity.dart +++ /dev/null @@ -1,33 +0,0 @@ -import 'package:flutter/material.dart'; - -import '../../theme/theme.dart'; - -class Quantity extends StatelessWidget { - const Quantity({ - super.key, - required this.value, - }); - - final int value; - - @override - Widget build(BuildContext context) { - return Container( - padding: EdgeInsets.all(AppTheme.of(context).spacing.extraSmall + 1), - width: AppTheme.of(context).spacing.extraExtraExtraLarge, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular( - AppTheme.of(context).radius.full, - ), - border: Border.all( - color: AppTheme.of(context).border.highEmphasis, - ), - ), - child: Text( - value.toString(), - textAlign: TextAlign.center, - style: AppTheme.of(context).typography.bodyMedium16, - ), - ); - } -} diff --git a/lib/basket/widgets/summary.dart b/lib/basket/widgets/summary.dart deleted file mode 100644 index d724c94..0000000 --- a/lib/basket/widgets/summary.dart +++ /dev/null @@ -1,79 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; - -import '../../l10n/app_localizations.dart'; -import '../../theme/app_theme.dart'; - -class Summary extends StatelessWidget { - const Summary({ - super.key, - required this.subTotal, - required this.delivery, - }); - - final double subTotal; - final double delivery; - - double get total => subTotal + delivery; - - @override - Widget build(BuildContext context) { - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - Container( - height: 1, - color: AppTheme.of(context).border.highEmphasis, - ), - SizedBox( - height: AppTheme.of(context).spacing.small, - ), - Row( - children: [ - Text( - AppLocalizations.of(context)!.basketSubtotal, - style: AppTheme.of(context).typography.subheadingRegular16, - ), - Expanded(child: Container()), - Text( - '\$${subTotal.toStringAsFixed(2)}', - style: AppTheme.of(context).typography.subheadingRegular16, - ), - ], - ), - SizedBox( - height: AppTheme.of(context).spacing.small, - ), - Row( - children: [ - Text( - AppLocalizations.of(context)!.basketDelivery, - style: AppTheme.of(context).typography.subheadingRegular16, - ), - Expanded(child: Container()), - Text( - '\$${delivery.toStringAsFixed(2)}', - style: AppTheme.of(context).typography.subheadingRegular16, - ), - ], - ), - SizedBox( - height: AppTheme.of(context).spacing.small, - ), - Row( - children: [ - Text( - AppLocalizations.of(context)!.basketTotal, - style: AppTheme.of(context).typography.subheadingMedium20, - ), - Expanded(child: Container()), - Text( - '\$${total.toStringAsFixed(2)}', - style: AppTheme.of(context).typography.subheadingMedium20, - ), - ], - ), - ], - ); - } -} diff --git a/lib/basket/widgets/widgets.dart b/lib/basket/widgets/widgets.dart deleted file mode 100644 index f9fbe92..0000000 --- a/lib/basket/widgets/widgets.dart +++ /dev/null @@ -1,4 +0,0 @@ -export 'basket_card.dart'; -export 'empty_basket_placeholder.dart'; -export 'quantity.dart'; -export 'summary.dart'; diff --git a/lib/core/app_bar.dart b/lib/core/app_bar.dart deleted file mode 100644 index 65ccce1..0000000 --- a/lib/core/app_bar.dart +++ /dev/null @@ -1,58 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; - -import '../theme/theme.dart'; -import 'app_icon.dart'; -import 'badge.dart' as core; - -class AppBar extends StatelessWidget { - const AppBar({ - super.key, - required this.title, - required this.basketSize, - }); - - final String title; - final int basketSize; - - @override - Widget build(BuildContext context) { - return Padding( - padding: EdgeInsets.symmetric( - horizontal: AppTheme.of(context).spacing.extraSmall, - vertical: AppTheme.of(context).spacing.small, - ), - child: Row( - children: [ - const AppIcon( - icon: Icons.menu_rounded, - ), - Expanded( - child: Align( - child: Text( - title, - style: AppTheme.of(context).typography.logoSemiBold20, - ), - ), - ), - Stack( - children: [ - AppIcon( - icon: Icons.shopping_bag_outlined, - onPressed: () => context.go('/basket'), - ), - if (basketSize > 0) - Positioned( - right: 6, - top: 6, - child: core.Badge( - number: basketSize, - ), - ), - ], - ), - ], - ), - ); - } -} diff --git a/lib/core/app_icon.dart b/lib/core/app_icon.dart deleted file mode 100644 index 1db0313..0000000 --- a/lib/core/app_icon.dart +++ /dev/null @@ -1,31 +0,0 @@ -import 'package:flutter/material.dart'; - -import '../theme/theme.dart'; - -class AppIcon extends StatelessWidget { - const AppIcon({ - super.key, - required this.icon, - this.onPressed, - }); - - final IconData icon; - final VoidCallback? onPressed; - - @override - Widget build(BuildContext context) { - return GestureDetector( - onTap: onPressed, - child: Padding( - padding: EdgeInsets.all( - AppTheme.of(context).spacing.small, - ), - child: Icon( - icon, - color: AppTheme.of(context).surface.secondary, - size: 24, - ), - ), - ); - } -} diff --git a/lib/core/badge.dart b/lib/core/badge.dart deleted file mode 100644 index c5c9110..0000000 --- a/lib/core/badge.dart +++ /dev/null @@ -1,27 +0,0 @@ -import 'package:flutter/material.dart'; - -import '../theme/theme.dart'; - -class Badge extends StatelessWidget { - const Badge({ - super.key, - required this.number, - }); - - final int number; - - @override - Widget build(BuildContext context) { - return Container( - width: 14, - height: 14, - padding: const EdgeInsets.all(1), - decoration: BoxDecoration( - color: AppTheme.of(context).surface.feature, - borderRadius: BorderRadius.circular( - AppTheme.of(context).radius.full, - ), - ), - ); - } -} diff --git a/lib/core/core.dart b/lib/core/core.dart deleted file mode 100644 index 098e07b..0000000 --- a/lib/core/core.dart +++ /dev/null @@ -1,6 +0,0 @@ -export 'app_bar.dart'; -export 'app_icon.dart'; -export 'badge.dart'; -export 'card.dart'; -export 'primary_button.dart'; -export 'quantity_button.dart'; diff --git a/lib/core/primary_button.dart b/lib/core/primary_button.dart deleted file mode 100644 index 17683f8..0000000 --- a/lib/core/primary_button.dart +++ /dev/null @@ -1,45 +0,0 @@ -import 'package:flutter/material.dart'; -import '../theme/app_theme.dart'; - -class PrimaryButton extends StatelessWidget { - const PrimaryButton({ - super.key, - required this.content, - }); - - final String content; - - @override - Widget build(BuildContext context) { - return Container( - padding: EdgeInsets.symmetric( - vertical: AppTheme.of(context).spacing.medium, - horizontal: AppTheme.of(context).spacing.extraLarge, - ), - decoration: BoxDecoration( - color: AppTheme.of(context).surface.brand, - borderRadius: BorderRadius.circular( - AppTheme.of(context).radius.full, - ), - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - content, - style: AppTheme.of(context).typography.labelMedium14.copyWith( - color: AppTheme.of(context).surface.light, - ), - ), - SizedBox( - width: AppTheme.of(context).spacing.small, - ), - Icon( - Icons.arrow_forward_rounded, - color: AppTheme.of(context).surface.light, - ), - ], - ), - ); - } -} diff --git a/lib/core/quantity_button.dart b/lib/core/quantity_button.dart deleted file mode 100644 index 3c143ef..0000000 --- a/lib/core/quantity_button.dart +++ /dev/null @@ -1,46 +0,0 @@ -import 'package:flutter/material.dart'; -import '../theme/app_theme.dart'; - -class QuantityButton extends StatelessWidget { - const QuantityButton({ - super.key, - required this.icon, - this.onPressed, - }); - - const QuantityButton.add({ - super.key, - this.onPressed, - }) : icon = Icons.add_rounded; - - const QuantityButton.remove({ - super.key, - this.onPressed, - }) : icon = Icons.remove_rounded; - - final VoidCallback? onPressed; - final IconData icon; - - @override - Widget build(BuildContext context) { - return GestureDetector( - onTap: onPressed, - child: Container( - padding: EdgeInsets.all(AppTheme.of(context).spacing.extraSmall), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular( - AppTheme.of(context).radius.full, - ), - border: Border.all( - color: AppTheme.of(context).border.highEmphasis, - ), - ), - child: Icon( - icon, - size: 22, - color: AppTheme.of(context).surface.invert, - ), - ), - ); - } -} diff --git a/lib/features/basket/basket.dart b/lib/features/basket/basket.dart new file mode 100644 index 0000000..b366abf --- /dev/null +++ b/lib/features/basket/basket.dart @@ -0,0 +1,3 @@ +export 'state/state.dart'; +export 'views/basket_view.dart'; +export 'widgets/widgets.dart'; diff --git a/lib/basket/state/basket_scope.dart b/lib/features/basket/state/basket_scope.dart similarity index 100% rename from lib/basket/state/basket_scope.dart rename to lib/features/basket/state/basket_scope.dart diff --git a/lib/basket/state/basket_state.dart b/lib/features/basket/state/basket_state.dart similarity index 96% rename from lib/basket/state/basket_state.dart rename to lib/features/basket/state/basket_state.dart index 06e3488..074f576 100644 --- a/lib/basket/state/basket_state.dart +++ b/lib/features/basket/state/basket_state.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -import '../../repositories/fruit.dart'; +import '../../../repositories/fruit.dart'; import 'basket_scope.dart'; class ProductOrder { diff --git a/lib/features/basket/state/state.dart b/lib/features/basket/state/state.dart new file mode 100644 index 0000000..2cd2848 --- /dev/null +++ b/lib/features/basket/state/state.dart @@ -0,0 +1,2 @@ +export 'basket_scope.dart'; +export 'basket_state.dart'; diff --git a/lib/features/basket/views/basket_view.dart b/lib/features/basket/views/basket_view.dart new file mode 100644 index 0000000..217ef86 --- /dev/null +++ b/lib/features/basket/views/basket_view.dart @@ -0,0 +1,83 @@ +import 'package:flutter/widgets.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; + +import '../../../l10n/app_localizations.dart'; +import '../../../repositories/fruit.dart'; +import '../../../ui/ui.dart'; +import '../basket.dart'; + +class BasketView extends StatelessWidget { + const BasketView({ + super.key, + required this.basket, + required this.delivery, + required this.subTotal, + }); + + final Map basket; + final double delivery; + final double subTotal; + + Widget _buildFilledPage(BuildContext context) { + return ListView.separated( + key: ValueKey(basket), + itemCount: basket.length, + separatorBuilder: (context, index) { + return SizedBox( + height: AppTheme.of(context).spacing.m, + ); + }, + itemBuilder: (context, index) { + final fruit = basket.keys.elementAt(index); + return BasketCard( + fruit: fruit, + count: basket[fruit]!.quantity, + onFruitAdded: () => BasketState.of(context).addFruit(fruit), + onFruitRemoved: () => BasketState.of(context).removeFruit(fruit), + ); + }, + ); + } + + @override + Widget build(BuildContext context) { + return basket.isEmpty + ? PageShell( + child: Column( + children: [ + Expanded( + child: Center( + child: EmptyState( + icon: FontAwesomeIcons.basketShopping, + title: AppLocalizations.of(context)!.basketEmpty, + message: AppLocalizations.of(context)!.basketEmptyMessage, + ), + ), + ), + PrimaryButton( + content: AppLocalizations.of(context)!.startShopping, + ), + ], + ), + ) + : PageShell( + header: AppLocalizations.of(context)!.basketHeadline, + child: Column( + children: [ + Expanded(child: _buildFilledPage(context)), + Summary( + subTotal: subTotal, + delivery: delivery, + ), + SizedBox( + height: AppTheme.of(context).spacing.xs, + ), + PrimaryButton( + content: + AppLocalizations.of(context)!.basketContinueToShipping, + ), + ], + ), + ); + } +} diff --git a/lib/features/basket/widgets/basket_card.dart b/lib/features/basket/widgets/basket_card.dart new file mode 100644 index 0000000..805412e --- /dev/null +++ b/lib/features/basket/widgets/basket_card.dart @@ -0,0 +1,76 @@ +import 'package:flutter/widgets.dart'; + +import '../../../repositories/fruit.dart'; +import '../../../ui/ui.dart'; + +class BasketCard extends StatelessWidget { + const BasketCard({ + super.key, + required this.fruit, + required this.count, + required this.onFruitAdded, + required this.onFruitRemoved, + }); + + final Fruit fruit; + final int count; + final VoidCallback onFruitAdded; + final VoidCallback onFruitRemoved; + + double get total => fruit.price * count; + + @override + Widget build(BuildContext context) { + return Card( + child: Row( + children: [ + ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(AppTheme.of(context).radius.sm), + bottomLeft: Radius.circular(AppTheme.of(context).radius.sm), + ), + child: SizedBox( + height: 72, + width: 76, + child: Image.network( + fruit.imageUrl, + fit: BoxFit.cover, + ), + ), + ), + Expanded( + child: Padding( + padding: EdgeInsets.all(AppTheme.of(context).spacing.xs), + child: Row( + children: [ + Expanded( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + fruit.name, + style: AppTheme.of(context).typography.headingLarge, + ), + SizedBox(height: AppTheme.of(context).spacing.xxs), + Text( + '\$${fruit.price.toStringAsFixed(2)}', + style: AppTheme.of(context).typography.bodyMedium, + ), + ], + ), + ), + Counter( + value: count, + onIncrement: onFruitAdded, + onDecrement: onFruitRemoved, + ), + ], + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/features/basket/widgets/cost_item_row.dart b/lib/features/basket/widgets/cost_item_row.dart new file mode 100644 index 0000000..c50db44 --- /dev/null +++ b/lib/features/basket/widgets/cost_item_row.dart @@ -0,0 +1,31 @@ +import 'package:flutter/material.dart'; + +import '../../../ui/ui.dart'; + +class CostItemRow extends StatelessWidget { + const CostItemRow({ + super.key, + required this.label, + required this.cost, + }); + + final String label; + final double cost; + + @override + Widget build(BuildContext context) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + label, + style: AppTheme.of(context).typography.subheadingSmall, + ), + Text( + '\$${cost.toStringAsFixed(2)}', + style: AppTheme.of(context).typography.subheadingSmall, + ), + ], + ); + } +} diff --git a/lib/features/basket/widgets/summary.dart b/lib/features/basket/widgets/summary.dart new file mode 100644 index 0000000..d6f7c6f --- /dev/null +++ b/lib/features/basket/widgets/summary.dart @@ -0,0 +1,49 @@ +import 'package:flutter/widgets.dart'; + +import '../../../l10n/app_localizations.dart'; +import '../../../ui/ui.dart'; +import 'cost_item_row.dart'; + +class Summary extends StatelessWidget { + const Summary({ + super.key, + required this.subTotal, + required this.delivery, + }); + + final double subTotal; + final double delivery; + + double get total => subTotal + delivery; + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + const Divider(), + SizedBox( + height: AppTheme.of(context).spacing.xxs, + ), + CostItemRow( + label: AppLocalizations.of(context)!.basketSubtotal, + cost: subTotal, + ), + SizedBox( + height: AppTheme.of(context).spacing.xxs, + ), + CostItemRow( + label: AppLocalizations.of(context)!.basketDelivery, + cost: delivery, + ), + SizedBox( + height: AppTheme.of(context).spacing.xxs, + ), + CostItemRow( + label: AppLocalizations.of(context)!.basketTotal, + cost: total, + ), + ], + ); + } +} diff --git a/lib/features/basket/widgets/widgets.dart b/lib/features/basket/widgets/widgets.dart new file mode 100644 index 0000000..27f67e0 --- /dev/null +++ b/lib/features/basket/widgets/widgets.dart @@ -0,0 +1,3 @@ +export 'basket_card.dart'; +export 'cost_item_row.dart'; +export 'summary.dart'; diff --git a/lib/features/features.dart b/lib/features/features.dart new file mode 100644 index 0000000..49c5a6f --- /dev/null +++ b/lib/features/features.dart @@ -0,0 +1 @@ +export 'basket/basket.dart'; diff --git a/lib/home/home.dart b/lib/home/home.dart deleted file mode 100644 index d4c09f0..0000000 --- a/lib/home/home.dart +++ /dev/null @@ -1,3 +0,0 @@ -export 'home_screen.dart'; -export 'widgets/add_basket_button.dart'; -export 'widgets/fruit_card.dart'; diff --git a/lib/home/home_screen.dart b/lib/home/home_screen.dart deleted file mode 100644 index 6b1287f..0000000 --- a/lib/home/home_screen.dart +++ /dev/null @@ -1,78 +0,0 @@ -import 'dart:math'; - -import 'package:flutter/material.dart'; -import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; - -import '../basket/state/basket_state.dart'; -import '../core/core.dart' as core; -import '../l10n/app_localizations.dart'; -import '../repositories/fruit.dart'; -import '../theme/theme.dart'; -import 'widgets/fruit_card.dart'; - -class HomeScreen extends StatelessWidget { - const HomeScreen({ - super.key, - required this.fruits, - }); - - final List fruits; - - @override - Widget build(BuildContext context) { - final basketState = BasketState.of(context); - - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - core.AppBar( - title: 'Grocery App', - basketSize: basketState.store.length, - ), - SizedBox( - height: AppTheme.of(context).spacing.large, - ), - Expanded( - child: Padding( - padding: EdgeInsets.symmetric( - horizontal: AppTheme.of(context).spacing.medium, - ), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - AppLocalizations.of(context)!.fruitsHeadline, - style: AppTheme.of(context).typography.displayRegular32, - ), - SizedBox( - height: AppTheme.of(context).spacing.large, - ), - Expanded( - child: LayoutBuilder( - builder: (context, constraints) { - return AlignedGridView.count( - padding: EdgeInsets.zero, - crossAxisCount: max(1, constraints.maxWidth ~/ 300), - mainAxisSpacing: AppTheme.of(context).spacing.medium, - crossAxisSpacing: AppTheme.of(context).spacing.medium, - itemCount: fruits.length, - itemBuilder: (context, index) { - final fruit = fruits[index]; - return FruitCard( - fruit: fruit, - onFruitAdded: () => basketState.addFruit(fruit), - ); - }, - ); - }, - ), - ), - ], - ), - ), - ), - ], - ); - } -} diff --git a/lib/home/widgets/add_basket_button.dart b/lib/home/widgets/add_basket_button.dart deleted file mode 100644 index 844068c..0000000 --- a/lib/home/widgets/add_basket_button.dart +++ /dev/null @@ -1,39 +0,0 @@ -import 'package:flutter/material.dart'; -import '../../theme/theme.dart'; - -class AddBasketButton extends StatelessWidget { - const AddBasketButton({ - super.key, - required this.icon, - this.onPressed, - }); - - final IconData icon; - final VoidCallback? onPressed; - - @override - Widget build(BuildContext context) { - return GestureDetector( - onTap: onPressed, - child: Container( - padding: EdgeInsets.all( - AppTheme.of(context).spacing.small, - ), - decoration: BoxDecoration( - color: AppTheme.of(context).surface.primary, - borderRadius: BorderRadius.circular( - AppTheme.of(context).radius.full, - ), - border: Border.all( - color: AppTheme.of(context).border.highEmphasis, - ), - ), - child: Icon( - icon, - size: 24, - color: AppTheme.of(context).surface.secondary, - ), - ), - ); - } -} diff --git a/lib/home/widgets/fruit_card.dart b/lib/home/widgets/fruit_card.dart deleted file mode 100644 index 4fad54c..0000000 --- a/lib/home/widgets/fruit_card.dart +++ /dev/null @@ -1,114 +0,0 @@ -import 'package:flutter/material.dart'; - -import '../../core/core.dart' as core; -import '../../l10n/app_localizations.dart'; -import '../../repositories/fruit.dart'; -import '../../theme/app_theme.dart'; -import 'add_basket_button.dart'; - -class FruitCard extends StatelessWidget { - const FruitCard({ - super.key, - required this.fruit, - this.onFruitAdded, - }); - - final Fruit fruit; - final VoidCallback? onFruitAdded; - - @override - Widget build(BuildContext context) { - return core.Card( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Stack( - children: [ - Container( - height: 200, - decoration: BoxDecoration( - borderRadius: BorderRadius.only( - topLeft: Radius.circular( - AppTheme.of(context).radius.small, - ), - topRight: Radius.circular( - AppTheme.of(context).radius.small, - ), - ), - border: Border.all( - color: AppTheme.of(context).border.lowEmphasis, - width: 0, - ), - image: DecorationImage( - fit: BoxFit.fitWidth, - image: NetworkImage(fruit.imageUrl), - ), - ), - ), - Positioned( - right: 6, - top: 6, - child: AddBasketButton( - icon: Icons.shopping_bag_outlined, - onPressed: onFruitAdded, - ), - ), - ], - ), - Container( - padding: EdgeInsets.all(AppTheme.of(context).spacing.medium), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Row( - children: [ - Expanded( - child: Text( - fruit.name, - style: - AppTheme.of(context).typography.headingSemibold20, - ), - ), - Text( - '\$${fruit.price}/${AppLocalizations.of(context)!.unit}', - style: AppTheme.of(context).typography.bodyMedium16, - ), - ], - ), - SizedBox( - height: AppTheme.of(context).spacing.medium, - ), - Text( - fruit.origin, - style: AppTheme.of(context).typography.bodyRegular14, - ), - SizedBox( - height: AppTheme.of(context).spacing.extraSmall, - ), - Row( - children: [ - Text( - fruit.farm, - textAlign: TextAlign.center, - style: AppTheme.of(context).typography.bodyMedium16, - ), - SizedBox( - width: AppTheme.of(context).spacing.extraSmall, - ), - Icon( - Icons.arrow_forward_rounded, - size: 16, - color: AppTheme.of(context).surface.secondary, - ), - ], - ), - ], - ), - ), - ], - ), - ); - } -} diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index fca528c..2d8596e 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -8,5 +8,8 @@ "basketDelivery": "Versand", "basketEmpty": "Dein Einkaufskorb ist leer", "basketEmptyMessage": "Der Einkaufskorb öffnet das Tor zu einer Welt voller grenzenloser Möglichkeiten und wartet geduldig auf deine Auswahl, um zum Leben erweckt zu werden.", - "startShopping": "Beginn mit dem Einkauf" + "startShopping": "Beginn mit dem Einkauf", + "navigationShopLabel": "Shop", + "navigationBasketLabel": "Warenkorb", + "navigationUserLabel": "Nutzer" } diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 4a96dcd..53c978f 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -8,5 +8,8 @@ "basketDelivery": "Delivery", "basketEmpty": "Your Basket is empty", "basketEmptyMessage": "The basket is a gateway to a world of infinite possibilities, patiently awaiting your selection to bring it to life.", - "startShopping": "Start shopping" + "startShopping": "Start shopping", + "navigationShopLabel": "Shop", + "navigationBasketLabel": "Basket", + "navigationUserLabel": "User" } diff --git a/lib/theme/app_theme.dart b/lib/theme/app_theme.dart deleted file mode 100644 index 5efb2d8..0000000 --- a/lib/theme/app_theme.dart +++ /dev/null @@ -1,108 +0,0 @@ -import 'package:flutter/widgets.dart'; -import 'typography_theme_data.dart'; - -class AppTheme extends InheritedWidget { - const AppTheme({ - super.key, - required this.data, - required super.child, - }); - - final AppThemeData data; - - static AppThemeData of(BuildContext context) { - final widget = context.dependOnInheritedWidgetOfExactType(); - return widget!.data; - } - - @override - bool updateShouldNotify(covariant AppTheme oldWidget) { - return data != oldWidget.data; - } -} - -class SpacingThemeData { - const SpacingThemeData({ - this.none = 0, - this.extraSmall = 4, - this.small = 8, - this.medium = 16, - this.large = 24, - this.extraLarge = 32, - this.extraExtraLarge = 40, - this.extraExtraExtraLarge = 56, - }); - final double none; - final double extraSmall; - final double small; - final double medium; - final double large; - final double extraLarge; - final double extraExtraLarge; - final double extraExtraExtraLarge; -} - -class RadiusThemeData { - const RadiusThemeData({ - this.none = 0, - this.extraSmall = 4, - this.small = 8, - this.medium = 12, - this.large = 16, - this.extraLarge = 28, - this.extraExtraLarge = 32, - this.full = 100, - }); - final double none; - final double extraSmall; - final double small; - final double medium; - final double large; - final double extraLarge; - final double extraExtraLarge; - final double full; -} - -class AppThemeData { - const AppThemeData({ - required this.typography, - required this.surface, - required this.border, - this.radius = const RadiusThemeData(), - this.spacing = const SpacingThemeData(), - }); - - final SpacingThemeData spacing; - final TypographyThemeData typography; - final RadiusThemeData radius; - final BorderThemeData border; - final SurfaceThemeData surface; -} - -class BorderThemeData { - const BorderThemeData({ - required this.lowEmphasis, - required this.highEmphasis, - }); - final Color lowEmphasis; - final Color highEmphasis; -} - -class SurfaceThemeData { - SurfaceThemeData({ - required this.primary, - required this.secondary, - required this.invert, - required this.light, - required this.tertiary, - required this.brand, - required this.feature, - }); - final Color primary; - final Color secondary; - final Color invert; - final Color light; - final Color tertiary; - final Color brand; - final Color feature; -} diff --git a/lib/theme/dark.dart b/lib/theme/dark.dart deleted file mode 100644 index 925c1ce..0000000 --- a/lib/theme/dark.dart +++ /dev/null @@ -1,22 +0,0 @@ -import 'package:flutter/widgets.dart'; -import 'theme.dart'; -import 'typography_theme_data.dart'; - -final darkTheme = AppThemeData( - typography: TypographyThemeData.fromColor( - const Color(0xFFEAE0D5), - ), - surface: SurfaceThemeData( - primary: const Color(0xFF202020), - secondary: const Color(0xFFAFAFAF), - invert: const Color(0xFFEAE0D5), - light: const Color(0xFF343434), - tertiary: const Color(0xFFD3D3D3), - brand: const Color(0xFFA0EFFF), - feature: const Color(0xFFFFDBD1), - ), - border: const BorderThemeData( - lowEmphasis: Color(0xFF4D4D4D), - highEmphasis: Color(0xFF7C7C7C), - ), -); diff --git a/lib/theme/light.dart b/lib/theme/light.dart deleted file mode 100644 index 37c06e1..0000000 --- a/lib/theme/light.dart +++ /dev/null @@ -1,22 +0,0 @@ -import 'package:flutter/widgets.dart'; -import 'theme.dart'; -import 'typography_theme_data.dart'; - -final lightTheme = AppThemeData( - typography: TypographyThemeData.fromColor( - const Color(0xFF202020), - ), - surface: SurfaceThemeData( - primary: const Color(0xFFEAE0D5), - secondary: const Color(0xFF343434), - invert: const Color(0xFF202020), - light: const Color(0xFFF2ECE6), - tertiary: const Color(0xFF7C7C7C), - brand: const Color(0xFF15616D), - feature: const Color(0xFF78290F), - ), - border: const BorderThemeData( - lowEmphasis: Color(0xFFD3D3D3), - highEmphasis: Color(0xFF7C7C7C), - ), -); diff --git a/lib/theme/theme.dart b/lib/theme/theme.dart deleted file mode 100644 index 68d23fd..0000000 --- a/lib/theme/theme.dart +++ /dev/null @@ -1,3 +0,0 @@ -export 'app_theme.dart'; -export 'dark.dart'; -export 'light.dart'; diff --git a/lib/theme/typography_theme_data.dart b/lib/theme/typography_theme_data.dart deleted file mode 100644 index 9abce82..0000000 --- a/lib/theme/typography_theme_data.dart +++ /dev/null @@ -1,121 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:google_fonts/google_fonts.dart'; - -class TypographyThemeData { - const TypographyThemeData({ - required this.logoSemiBold20, - required this.displayRegular32, - required this.headingSemibold20, - required this.headingMedium24, - required this.bodyRegular12, - required this.bodyRegular14, - required this.bodyMedium16, - required this.bodySemiBold16, - required this.labelMedium11, - required this.labelMedium14, - required this.subheadingRegular16, - required this.subheadingMedium20, - }); - - factory TypographyThemeData.fromColor( - Color fontColor, - ) { - return TypographyThemeData( - logoSemiBold20: GoogleFonts.newsreader( - fontSize: 20, - fontWeight: FontWeight.w600, - color: fontColor, - letterSpacing: 0.5, - height: 20 / 24, - ), - displayRegular32: GoogleFonts.lora( - fontSize: 32, - fontWeight: FontWeight.w400, - color: fontColor, - height: 28 / 32, - ), - headingSemibold20: GoogleFonts.poppins( - fontSize: 20, - fontWeight: FontWeight.w600, - color: fontColor, - letterSpacing: 0.5, - height: 24 / 20, - ), - headingMedium24: GoogleFonts.poppins( - fontSize: 24, - fontWeight: FontWeight.w500, - color: fontColor, - height: 28 / 24, - ), - bodyRegular12: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w400, - color: fontColor, - letterSpacing: 0.5, - height: 24 / 12, - ), - bodyRegular14: GoogleFonts.poppins( - fontSize: 14, - fontWeight: FontWeight.w400, - color: fontColor, - letterSpacing: 0.25, - height: 20 / 14, - ), - bodyMedium16: GoogleFonts.poppins( - fontSize: 16, - fontWeight: FontWeight.w500, - color: fontColor, - letterSpacing: 0.25, - height: 20 / 16, - ), - bodySemiBold16: GoogleFonts.poppins( - fontSize: 16, - fontWeight: FontWeight.w600, - color: fontColor, - letterSpacing: 0.5, - height: 24 / 16, - ), - labelMedium11: GoogleFonts.poppins( - fontSize: 11, - fontWeight: FontWeight.w500, - color: fontColor, - letterSpacing: 0.1, - height: 16 / 11, - ), - labelMedium14: GoogleFonts.poppins( - fontSize: 14, - fontWeight: FontWeight.w500, - color: fontColor, - letterSpacing: 0.1, - height: 20 / 14, - ), - subheadingRegular16: GoogleFonts.poppins( - fontSize: 16, - fontWeight: FontWeight.w400, - color: fontColor, - letterSpacing: 0.5, - height: 24 / 16, - ), - subheadingMedium20: GoogleFonts.poppins( - fontSize: 20, - fontWeight: FontWeight.w500, - color: fontColor, - letterSpacing: 0.5, - height: 24 / 20, - ), - ); - } - - final TextStyle displayRegular32; - final TextStyle headingSemibold20; - final TextStyle headingMedium24; - final TextStyle bodyRegular12; - final TextStyle bodyRegular14; - final TextStyle bodyMedium16; - final TextStyle bodySemiBold16; - final TextStyle labelMedium11; - final TextStyle labelMedium14; - final TextStyle subheadingRegular16; - final TextStyle subheadingMedium20; - final TextStyle logoSemiBold20; -} diff --git a/lib/ui/foundation/color.dart b/lib/ui/foundation/color.dart new file mode 100644 index 0000000..a608add --- /dev/null +++ b/lib/ui/foundation/color.dart @@ -0,0 +1,68 @@ +import 'package:flutter/material.dart'; + +class DesignSystemColor extends ColorSwatch { + const DesignSystemColor._(super.primary, super.swatch); + + static const DesignSystemColor primary = DesignSystemColor._( + 0xFFEAE0D5, + { + 100: Color(0xFFEAE0D5), + 200: Color(0xFFCBC3B9), + 300: Color(0xFFAEA69E), + 400: Color(0xFF918A83), + 500: Color(0xFF75706A), + 600: Color(0xFF5A5651), + 700: Color(0xFF413E3A), + 800: Color(0xFF292724), + 900: Color(0xFF131210), + }, + ); + + static const DesignSystemColor brand = DesignSystemColor._( + 0xFF36CBE2, + { + 100: Color(0xFF36CBE2), + 200: Color(0xFF2DAEC2), + 300: Color(0xFF299FB2), + 400: Color(0xFF2491A2), + 500: Color(0xFF1C7684), + 600: Color(0xFF15616D), + 700: Color(0xFF0C424B), + 800: Color(0xFF0A363D), + 900: Color(0xFF071F23), + }, + ); + + static const DesignSystemColor grey = DesignSystemColor._( + 0xFFFFFFFF, + { + 100: Color(0xFFFFFFFF), + 200: Color(0xFFF2F2F2), + 300: Color(0xFFE5E5E5), + 400: Color(0xFFB2B2B2), + 500: Color(0xFF8C8C8C), + 600: Color(0xFF595959), + 700: Color(0xFF404040), + 800: Color(0xFF262626), + 900: Color(0xFF000000), + }, + ); + + Color get shade100 => this[100]!; + + Color get shade200 => this[200]!; + + Color get shade300 => this[300]!; + + Color get shade400 => this[400]!; + + Color get shade500 => this[500]!; + + Color get shade600 => this[600]!; + + Color get shade700 => this[700]!; + + Color get shade800 => this[800]!; + + Color get shade900 => this[900]!; +} diff --git a/lib/ui/foundation/foundation.dart b/lib/ui/foundation/foundation.dart new file mode 100644 index 0000000..738323e --- /dev/null +++ b/lib/ui/foundation/foundation.dart @@ -0,0 +1,4 @@ +export 'color.dart'; +export 'radius.dart'; +export 'spacing.dart'; +export 'text_styles.dart'; diff --git a/lib/ui/foundation/radius.dart b/lib/ui/foundation/radius.dart new file mode 100644 index 0000000..b886f7e --- /dev/null +++ b/lib/ui/foundation/radius.dart @@ -0,0 +1,10 @@ +class DesignSystemRadius { + static const double none = 0; + static const double xs = 4; + static const double sm = 8; + static const double md = 12; + static const double lg = 16; + static const double xl = 28; + static const double xxl = 32; + static const double full = 999; +} diff --git a/lib/ui/foundation/spacing.dart b/lib/ui/foundation/spacing.dart new file mode 100644 index 0000000..dad35ea --- /dev/null +++ b/lib/ui/foundation/spacing.dart @@ -0,0 +1,11 @@ +class DesignSystemSpacing { + static const double zero = 0; + static const double xxs = 4; + static const double xs = 8; + static const double sm = 12; + static const double m = 16; + static const double l = 24; + static const double xl = 32; + static const double xxl = 48; + static const double xxxl = 64; +} diff --git a/lib/ui/foundation/text_styles.dart b/lib/ui/foundation/text_styles.dart new file mode 100644 index 0000000..b273196 --- /dev/null +++ b/lib/ui/foundation/text_styles.dart @@ -0,0 +1,85 @@ +// ignore_for_file: unused_field + +import 'package:flutter/widgets.dart'; +import 'package:google_fonts/google_fonts.dart'; + +class _FigmaFontLineHeight { + static const double xs = 16; + static const double sm = 24; + static const double md = 32; + static const double lg = 40; + static const double xl = 48; +} + +class _FigmaFontHeight { + static const double xs = 12; + static const double sm = 16; + static const double md = 18; + static const double lg = 20; + static const double xl = 24; + static const double xxl = 32; + static const double xxxl = 40; + static const double xxxxl = 48; +} + +class DesignSystemTextStyles { + final display = GoogleFonts.lora( + fontSize: _FigmaFontHeight.xxxl, + fontWeight: FontWeight.w500, + height: _FigmaFontLineHeight.xl / _FigmaFontHeight.xxxl, + ); + + final headingExtraLarge = GoogleFonts.poppins( + fontSize: _FigmaFontHeight.xl, + fontWeight: FontWeight.w500, + height: _FigmaFontLineHeight.md / _FigmaFontHeight.xl, + ); + + final headingLarge = GoogleFonts.poppins( + fontSize: _FigmaFontHeight.lg, + fontWeight: FontWeight.w500, + height: _FigmaFontLineHeight.sm / _FigmaFontHeight.lg, + ); + + final subheadingLarge = GoogleFonts.poppins( + fontSize: _FigmaFontHeight.lg, + fontWeight: FontWeight.w500, + height: _FigmaFontLineHeight.sm / _FigmaFontHeight.lg, + ); + + final subheadingSmall = GoogleFonts.poppins( + fontSize: _FigmaFontHeight.sm, + fontWeight: FontWeight.w400, + height: _FigmaFontLineHeight.sm / _FigmaFontHeight.sm, + ); + + final label = GoogleFonts.poppins( + fontSize: _FigmaFontHeight.sm, + fontWeight: FontWeight.w500, + height: _FigmaFontLineHeight.sm / _FigmaFontHeight.sm, + ); + + final bodySmall = GoogleFonts.poppins( + fontSize: _FigmaFontHeight.xs, + fontWeight: FontWeight.w400, + height: _FigmaFontLineHeight.xs / _FigmaFontHeight.xs, + ); + + final bodyRegular = GoogleFonts.poppins( + fontSize: _FigmaFontHeight.sm, + fontWeight: FontWeight.w400, + height: _FigmaFontLineHeight.sm / _FigmaFontHeight.sm, + ); + + final bodyMedium = GoogleFonts.poppins( + fontSize: _FigmaFontHeight.sm, + fontWeight: FontWeight.w500, + height: _FigmaFontLineHeight.sm / _FigmaFontHeight.sm, + ); + + final bodySemiBold = GoogleFonts.poppins( + fontSize: _FigmaFontHeight.sm, + fontWeight: FontWeight.w600, + height: _FigmaFontLineHeight.sm / _FigmaFontHeight.sm, + ); +} diff --git a/lib/ui/theme/app_theme.dart b/lib/ui/theme/app_theme.dart new file mode 100644 index 0000000..8481ae6 --- /dev/null +++ b/lib/ui/theme/app_theme.dart @@ -0,0 +1,23 @@ +import 'package:flutter/widgets.dart'; + +import 'app_theme_data.dart'; + +class AppTheme extends InheritedWidget { + const AppTheme({ + super.key, + required this.data, + required super.child, + }); + + final AppThemeData data; + + static AppThemeData of(BuildContext context) { + final widget = context.dependOnInheritedWidgetOfExactType(); + return widget!.data; + } + + @override + bool updateShouldNotify(covariant AppTheme oldWidget) { + return data != oldWidget.data; + } +} diff --git a/lib/ui/theme/app_theme_data.dart b/lib/ui/theme/app_theme_data.dart new file mode 100644 index 0000000..ddc9b6e --- /dev/null +++ b/lib/ui/theme/app_theme_data.dart @@ -0,0 +1,67 @@ +import '../ui.dart'; +import 'background_theme_data.dart'; +import 'border_theme_data.dart'; +import 'radius_theme_data.dart'; +import 'spacing_theme_data.dart'; +import 'text_theme_data.dart'; +import 'typography_theme_data.dart'; + +class AppThemeData { + AppThemeData({ + required this.typography, + required this.background, + required this.border, + required this.text, + this.radius = const RadiusThemeData(), + this.spacing = const SpacingThemeData(), + }); + + final SpacingThemeData spacing; + final TypographyThemeData typography; + final RadiusThemeData radius; + final BackgroundThemeData background; + final BorderThemeData border; + final TextThemeData text; + + static AppThemeData light = AppThemeData( + typography: TypographyThemeData.fromColor(DesignSystemColor.grey.shade800), + background: BackgroundThemeData( + primary: DesignSystemColor.primary.shade100, + primaryInactive: DesignSystemColor.primary.shade600, + secondary: DesignSystemColor.primary.shade200, + brand: DesignSystemColor.brand.shade600, + page: DesignSystemColor.primary.shade200, + ), + border: BorderThemeData( + color: DesignSystemColor.primary.shade200, + ), + text: TextThemeData( + primary: DesignSystemColor.grey.shade800, + secondary: DesignSystemColor.primary.shade800, + tertiary: DesignSystemColor.primary.shade600, + inactive: DesignSystemColor.grey.shade400, + inverse: DesignSystemColor.grey.shade200, + ), + ); + + static AppThemeData dark = AppThemeData( + typography: TypographyThemeData.fromColor(DesignSystemColor.grey.shade200), + background: BackgroundThemeData( + primary: DesignSystemColor.primary.shade900, + primaryInactive: DesignSystemColor.primary.shade600, + secondary: DesignSystemColor.primary.shade800, + brand: DesignSystemColor.brand.shade100, + page: DesignSystemColor.primary.shade800, + ), + border: BorderThemeData( + color: DesignSystemColor.primary.shade700, + ), + text: TextThemeData( + primary: DesignSystemColor.grey.shade200, + secondary: DesignSystemColor.primary.shade400, + tertiary: DesignSystemColor.primary.shade500, + inactive: DesignSystemColor.grey.shade400, + inverse: DesignSystemColor.grey.shade800, + ), + ); +} diff --git a/lib/ui/theme/background_theme_data.dart b/lib/ui/theme/background_theme_data.dart new file mode 100644 index 0000000..59b67a9 --- /dev/null +++ b/lib/ui/theme/background_theme_data.dart @@ -0,0 +1,17 @@ +import 'package:flutter/widgets.dart'; + +class BackgroundThemeData { + BackgroundThemeData({ + required this.primary, + required this.primaryInactive, + required this.secondary, + required this.brand, + required this.page, + }); + + final Color primary; + final Color primaryInactive; + final Color secondary; + final Color brand; + final Color page; +} diff --git a/lib/ui/theme/border_theme_data.dart b/lib/ui/theme/border_theme_data.dart new file mode 100644 index 0000000..5c0c1aa --- /dev/null +++ b/lib/ui/theme/border_theme_data.dart @@ -0,0 +1,6 @@ +import 'package:flutter/widgets.dart'; + +class BorderThemeData { + BorderThemeData({required this.color}); + final Color color; +} diff --git a/lib/ui/theme/radius_theme_data.dart b/lib/ui/theme/radius_theme_data.dart new file mode 100644 index 0000000..ff2ba8c --- /dev/null +++ b/lib/ui/theme/radius_theme_data.dart @@ -0,0 +1,23 @@ +import '../foundation/radius.dart'; + +class RadiusThemeData { + const RadiusThemeData({ + this.none = DesignSystemRadius.none, + this.xs = DesignSystemRadius.xs, + this.sm = DesignSystemRadius.sm, + this.md = DesignSystemRadius.md, + this.lg = DesignSystemRadius.lg, + this.xl = DesignSystemRadius.xl, + this.xxl = DesignSystemRadius.xxl, + this.full = DesignSystemRadius.full, + }); + + final double none; + final double xs; + final double sm; + final double md; + final double lg; + final double xl; + final double xxl; + final double full; +} diff --git a/lib/ui/theme/spacing_theme_data.dart b/lib/ui/theme/spacing_theme_data.dart new file mode 100644 index 0000000..3191616 --- /dev/null +++ b/lib/ui/theme/spacing_theme_data.dart @@ -0,0 +1,25 @@ +import '../ui.dart'; + +class SpacingThemeData { + const SpacingThemeData({ + this.zero = DesignSystemSpacing.zero, + this.xxs = DesignSystemSpacing.xxs, + this.xs = DesignSystemSpacing.xs, + this.sm = DesignSystemSpacing.sm, + this.m = DesignSystemSpacing.m, + this.l = DesignSystemSpacing.l, + this.xl = DesignSystemSpacing.xl, + this.xxl = DesignSystemSpacing.xxl, + this.xxxl = DesignSystemSpacing.xxxl, + }); + + final double zero; + final double xxs; + final double xs; + final double sm; + final double m; + final double l; + final double xl; + final double xxl; + final double xxxl; +} diff --git a/lib/ui/theme/text_theme_data.dart b/lib/ui/theme/text_theme_data.dart new file mode 100644 index 0000000..c4cd6e1 --- /dev/null +++ b/lib/ui/theme/text_theme_data.dart @@ -0,0 +1,17 @@ +import 'package:flutter/widgets.dart'; + +class TextThemeData { + const TextThemeData({ + required this.primary, + required this.secondary, + required this.tertiary, + required this.inactive, + required this.inverse, + }); + + final Color primary; + final Color secondary; + final Color tertiary; + final Color inactive; + final Color inverse; +} diff --git a/lib/ui/theme/theme.dart b/lib/ui/theme/theme.dart new file mode 100644 index 0000000..a1b613d --- /dev/null +++ b/lib/ui/theme/theme.dart @@ -0,0 +1,2 @@ +export 'app_theme.dart'; +export 'app_theme_data.dart'; diff --git a/lib/ui/theme/typography_theme_data.dart b/lib/ui/theme/typography_theme_data.dart new file mode 100644 index 0000000..bd7855d --- /dev/null +++ b/lib/ui/theme/typography_theme_data.dart @@ -0,0 +1,44 @@ +import 'package:flutter/widgets.dart'; + +import '../foundation/text_styles.dart'; + +class TypographyThemeData { + TypographyThemeData._({ + required this.display, + required this.headingExtraLarge, + required this.headingLarge, + required this.subheadingLarge, + required this.subheadingSmall, + required this.bodySemiBold, + required this.bodyMedium, + required this.bodyRegular, + required this.bodySmall, + required this.label, + }); + + factory TypographyThemeData.fromColor(Color color) { + final styles = DesignSystemTextStyles(); + return TypographyThemeData._( + display: styles.display.copyWith(color: color), + headingExtraLarge: styles.headingExtraLarge.copyWith(color: color), + headingLarge: styles.headingLarge.copyWith(color: color), + subheadingLarge: styles.subheadingLarge.copyWith(color: color), + subheadingSmall: styles.subheadingSmall.copyWith(color: color), + bodySemiBold: styles.bodySemiBold.copyWith(color: color), + bodyMedium: styles.bodyMedium.copyWith(color: color), + bodyRegular: styles.bodyRegular.copyWith(color: color), + bodySmall: styles.bodySmall.copyWith(color: color), + label: styles.label.copyWith(color: color), + ); + } + final TextStyle display; + final TextStyle headingExtraLarge; + final TextStyle headingLarge; + final TextStyle subheadingLarge; + final TextStyle subheadingSmall; + final TextStyle bodySemiBold; + final TextStyle bodyMedium; + final TextStyle bodyRegular; + final TextStyle bodySmall; + final TextStyle label; +} diff --git a/lib/ui/ui.dart b/lib/ui/ui.dart new file mode 100644 index 0000000..4eec42f --- /dev/null +++ b/lib/ui/ui.dart @@ -0,0 +1,3 @@ +export 'foundation/foundation.dart'; +export 'theme/theme.dart'; +export 'widgets/widgets.dart'; diff --git a/lib/ui/widgets/app_bar.dart b/lib/ui/widgets/app_bar.dart new file mode 100644 index 0000000..0c88767 --- /dev/null +++ b/lib/ui/widgets/app_bar.dart @@ -0,0 +1,36 @@ +import 'package:flutter/widgets.dart' hide Icon; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; + +import '../ui.dart'; + +class AppBar extends StatelessWidget { + const AppBar({ + super.key, + required this.title, + }); + + final String title; + + @override + Widget build(BuildContext context) { + return Padding( + padding: EdgeInsets.symmetric( + vertical: AppTheme.of(context).spacing.xs, + horizontal: AppTheme.of(context).spacing.sm, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + title, + style: AppTheme.of(context).typography.subheadingLarge, + ), + SizedBox( + width: AppTheme.of(context).spacing.xs, + ), + const Icon(FontAwesomeIcons.bell), + ], + ), + ); + } +} diff --git a/lib/ui/widgets/bottom_navigation_bar.dart b/lib/ui/widgets/bottom_navigation_bar.dart new file mode 100644 index 0000000..87239b6 --- /dev/null +++ b/lib/ui/widgets/bottom_navigation_bar.dart @@ -0,0 +1,42 @@ +import 'package:flutter/widgets.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; + +import '../../l10n/app_localizations.dart'; +import '../ui.dart'; + +class BottomNavigationBar extends StatelessWidget { + const BottomNavigationBar({super.key}); + + @override + Widget build(BuildContext context) { + return Padding( + padding: EdgeInsets.symmetric( + horizontal: AppTheme.of(context).spacing.l, + vertical: AppTheme.of(context).spacing.xxs, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + NavigationItem( + isSelected: false, + iconUnselected: FontAwesomeIcons.lemon, + iconSelected: FontAwesomeIcons.solidLemon, + text: AppLocalizations.of(context)!.navigationShopLabel, + ), + NavigationItem( + isSelected: true, + iconUnselected: FontAwesomeIcons.rectangleList, + iconSelected: FontAwesomeIcons.solidRectangleList, + text: AppLocalizations.of(context)!.navigationBasketLabel, + ), + NavigationItem( + isSelected: false, + iconUnselected: FontAwesomeIcons.user, + iconSelected: FontAwesomeIcons.user, + text: AppLocalizations.of(context)!.navigationUserLabel, + ), + ], + ), + ); + } +} diff --git a/lib/core/card.dart b/lib/ui/widgets/card.dart similarity index 68% rename from lib/core/card.dart rename to lib/ui/widgets/card.dart index 4e535c7..3bb312f 100644 --- a/lib/core/card.dart +++ b/lib/ui/widgets/card.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; -import '../theme/app_theme.dart'; + +import '../ui.dart'; class Card extends StatelessWidget { const Card({ @@ -13,12 +14,12 @@ class Card extends StatelessWidget { Widget build(BuildContext context) { return Container( decoration: BoxDecoration( - color: AppTheme.of(context).surface.light, + color: AppTheme.of(context).background.primary, borderRadius: BorderRadius.circular( - AppTheme.of(context).radius.small, + AppTheme.of(context).radius.sm, ), border: Border.all( - color: AppTheme.of(context).border.lowEmphasis, + color: AppTheme.of(context).border.color, ), ), child: child, diff --git a/lib/ui/widgets/counter.dart b/lib/ui/widgets/counter.dart new file mode 100644 index 0000000..24417b4 --- /dev/null +++ b/lib/ui/widgets/counter.dart @@ -0,0 +1,50 @@ +import 'package:flutter/widgets.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; + +import '../ui.dart'; + +class Counter extends StatelessWidget { + const Counter({ + super.key, + required this.value, + required this.onIncrement, + required this.onDecrement, + }); + + final int value; + final VoidCallback onIncrement; + final VoidCallback onDecrement; + + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( + color: AppTheme.of(context).background.secondary, + borderRadius: BorderRadius.circular(AppTheme.of(context).radius.full), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + icon: FontAwesomeIcons.minus, + onPressed: onDecrement, + ), + Padding( + padding: EdgeInsets.symmetric( + horizontal: AppTheme.of(context).spacing.xs, + vertical: AppTheme.of(context).spacing.xxs, + ), + child: Text( + value.toString(), + style: AppTheme.of(context).typography.bodyMedium, + ), + ), + IconButton( + icon: FontAwesomeIcons.plus, + onPressed: onIncrement, + ), + ], + ), + ); + } +} diff --git a/lib/ui/widgets/divider.dart b/lib/ui/widgets/divider.dart new file mode 100644 index 0000000..56dba9d --- /dev/null +++ b/lib/ui/widgets/divider.dart @@ -0,0 +1,15 @@ +import 'package:flutter/widgets.dart'; + +import '../ui.dart'; + +class Divider extends StatelessWidget { + const Divider({super.key}); + + @override + Widget build(BuildContext context) { + return Container( + height: 1, + color: AppTheme.of(context).border.color, + ); + } +} diff --git a/lib/ui/widgets/empty_state.dart b/lib/ui/widgets/empty_state.dart new file mode 100644 index 0000000..0c495f1 --- /dev/null +++ b/lib/ui/widgets/empty_state.dart @@ -0,0 +1,45 @@ +import 'package:flutter/material.dart' hide Icon; + +import '../ui.dart'; + +class EmptyState extends StatelessWidget { + const EmptyState({ + super.key, + required this.icon, + required this.title, + required this.message, + }); + + final IconData icon; + final String title; + final String message; + + @override + Widget build(BuildContext context) { + final spacing = AppTheme.of(context).spacing.sm; + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + icon, + scale: 2, + ), + SizedBox( + height: spacing, + ), + Text( + title, + style: AppTheme.of(context).typography.headingExtraLarge, + ), + SizedBox( + height: spacing, + ), + Text( + message, + style: AppTheme.of(context).typography.bodyRegular, + textAlign: TextAlign.center, + ), + ], + ); + } +} diff --git a/lib/ui/widgets/icon.dart b/lib/ui/widgets/icon.dart new file mode 100644 index 0000000..7327a24 --- /dev/null +++ b/lib/ui/widgets/icon.dart @@ -0,0 +1,27 @@ +import 'package:flutter/widgets.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; + +import '../ui.dart'; + +class Icon extends StatelessWidget { + const Icon( + this.icon, { + super.key, + this.scale = 1, + }); + + final IconData icon; + final double scale; + + @override + Widget build(BuildContext context) { + return Padding( + padding: EdgeInsets.all(AppTheme.of(context).spacing.xs), + child: FaIcon( + icon, + size: 20 * scale, + color: AppTheme.of(context).text.primary, + ), + ); + } +} diff --git a/lib/ui/widgets/icon_button.dart b/lib/ui/widgets/icon_button.dart new file mode 100644 index 0000000..36ed976 --- /dev/null +++ b/lib/ui/widgets/icon_button.dart @@ -0,0 +1,54 @@ +import 'package:flutter/widgets.dart' hide Icon; + +import '../ui.dart'; + +class IconButton extends StatefulWidget { + const IconButton({ + super.key, + required this.icon, + required this.onPressed, + }); + + final IconData icon; + final VoidCallback onPressed; + + @override + IconButtonState createState() => IconButtonState(); +} + +class IconButtonState extends State { + double _scale = 1; + + void _onTapDown(TapDownDetails details) { + setState(() { + _scale = 0.9; // Shrinks the icon slightly + }); + } + + void _onTapUp(TapUpDetails details) { + setState(() { + _scale = 1.0; // Restores the icon size + }); + } + + void _onTapCancel() { + setState(() { + _scale = 1.0; // Restores the icon size if the tap is canceled + }); + } + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: widget.onPressed, + onTapDown: _onTapDown, + onTapUp: _onTapUp, + onTapCancel: _onTapCancel, + child: AnimatedScale( + scale: _scale, + duration: const Duration(milliseconds: 100), + child: Icon(widget.icon), + ), + ); + } +} diff --git a/lib/ui/widgets/navigation_item.dart b/lib/ui/widgets/navigation_item.dart new file mode 100644 index 0000000..5133401 --- /dev/null +++ b/lib/ui/widgets/navigation_item.dart @@ -0,0 +1,28 @@ +import 'package:flutter/widgets.dart' hide Icon; + +import 'icon.dart'; + +class NavigationItem extends StatelessWidget { + const NavigationItem({ + super.key, + required this.isSelected, + required this.iconUnselected, + required this.iconSelected, + required this.text, + }); + + final bool isSelected; + final IconData iconUnselected; + final IconData iconSelected; + final String text; + + @override + Widget build(BuildContext context) { + return Column( + children: [ + if (isSelected) Icon(iconSelected) else Icon(iconUnselected), + Text(text), + ], + ); + } +} diff --git a/lib/ui/widgets/page_shell.dart b/lib/ui/widgets/page_shell.dart new file mode 100644 index 0000000..6dda650 --- /dev/null +++ b/lib/ui/widgets/page_shell.dart @@ -0,0 +1,42 @@ +import 'package:flutter/widgets.dart'; + +import '../ui.dart'; + +class PageShell extends StatelessWidget { + const PageShell({super.key, required this.child, this.header}); + + final Widget child; + final String? header; + + @override + Widget build(BuildContext context) { + final theme = AppTheme.of(context); + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const AppBar( + title: 'Groceries App', + ), + const Divider(), + if (header != null) + Padding( + padding: EdgeInsets.all(AppTheme.of(context).spacing.sm), + child: Text( + header!, + style: AppTheme.of(context).typography.headingLarge, + ), + ), + Expanded( + child: Padding( + padding: EdgeInsets.all( + theme.spacing.sm, + ), + child: child, + ), + ), + const Divider(), + const BottomNavigationBar(), + ], + ); + } +} diff --git a/lib/ui/widgets/primary_button.dart b/lib/ui/widgets/primary_button.dart new file mode 100644 index 0000000..046a9bf --- /dev/null +++ b/lib/ui/widgets/primary_button.dart @@ -0,0 +1,61 @@ +import 'package:flutter/material.dart'; + +import '../ui.dart'; + +class PrimaryButton extends StatelessWidget { + const PrimaryButton({ + super.key, + required this.content, + this.leading, + this.trailing, + }); + + final String content; + final Widget? leading; + final Widget? trailing; + + @override + Widget build(BuildContext context) { + return Container( + padding: EdgeInsets.symmetric( + vertical: AppTheme.of(context).spacing.sm, + horizontal: AppTheme.of(context).spacing.l, + ), + decoration: BoxDecoration( + color: AppTheme.of(context).background.brand, + borderRadius: BorderRadius.circular( + AppTheme.of(context).radius.full, + ), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + if (leading != null) + Row( + children: [ + leading!, + SizedBox( + width: AppTheme.of(context).spacing.xs, + ), + ], + ), + Text( + content, + style: AppTheme.of(context).typography.label.copyWith( + color: AppTheme.of(context).text.inverse, + ), + ), + if (trailing != null) + Row( + children: [ + SizedBox( + width: AppTheme.of(context).spacing.xs, + ), + trailing!, + ], + ), + ], + ), + ); + } +} diff --git a/lib/ui/widgets/responsive_layout.dart b/lib/ui/widgets/responsive_layout.dart new file mode 100644 index 0000000..88f9c81 --- /dev/null +++ b/lib/ui/widgets/responsive_layout.dart @@ -0,0 +1,50 @@ +// TODO maybe delete + +// import 'package:flutter/material.dart'; + +// import '../ui.dart'; + +// class ResponsiveLayout extends StatelessWidget { +// const ResponsiveLayout({ +// super.key, +// this.headline, +// required this.content, +// this.footer, +// }); + +// final String? headline; +// final Widget content; +// final Widget? footer; + +// @override +// Widget build(BuildContext context) { +// return Padding( +// padding: EdgeInsets.all(AppTheme.of(context).spacing.medium), +// child: Column( +// crossAxisAlignment: CrossAxisAlignment.start, +// children: [ +// if (headline != null) +// Column( +// crossAxisAlignment: CrossAxisAlignment.start, +// children: [ +// Text( +// headline!, +// style: AppTheme.of(context).typography.displayRegular32, +// ), +// SizedBox(height: AppTheme.of(context).spacing.large), +// ], +// ), +// Expanded(child: content), +// if (footer != null) +// Column( +// crossAxisAlignment: CrossAxisAlignment.start, +// children: [ +// SizedBox(height: AppTheme.of(context).spacing.large), +// footer!, +// ], +// ), +// ], +// ), +// ); +// } +// } diff --git a/lib/ui/widgets/widgets.dart b/lib/ui/widgets/widgets.dart new file mode 100644 index 0000000..976e0a8 --- /dev/null +++ b/lib/ui/widgets/widgets.dart @@ -0,0 +1,12 @@ +export 'app_bar.dart'; +export 'bottom_navigation_bar.dart'; +export 'card.dart'; +export 'counter.dart'; +export 'divider.dart'; +export 'empty_state.dart'; +export 'icon.dart'; +export 'icon_button.dart'; +export 'navigation_item.dart'; +export 'page_shell.dart'; +export 'primary_button.dart'; +export 'responsive_layout.dart'; diff --git a/pubspec.lock b/pubspec.lock index 5701e79..c8859c4 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -79,6 +79,14 @@ packages: description: flutter source: sdk version: "0.0.0" + font_awesome_flutter: + dependency: "direct main" + description: + name: font_awesome_flutter + sha256: "275ff26905134bcb59417cf60ad979136f1f8257f2f449914b2c3e05bbb4cd6f" + url: "https://pub.dev" + source: hosted + version: "10.7.0" go_router: dependency: "direct main" description: @@ -99,10 +107,10 @@ packages: dependency: transitive description: name: http - sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010 + sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938" url: "https://pub.dev" source: hosted - version: "1.2.2" + version: "1.2.1" http_parser: dependency: transitive description: @@ -155,18 +163,18 @@ packages: dependency: transitive description: name: path_provider - sha256: fec0d61223fba3154d87759e3cc27fe2c8dc498f6386c6d6fc80d1afdd1bf378 + sha256: c9e7d3a4cd1410877472158bee69963a4579f78b68c65a2b7d40d1a7a88bb161 url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.1.3" path_provider_android: dependency: transitive description: name: path_provider_android - sha256: "490539678396d4c3c0b06efdaab75ae60675c3e0c66f72bc04c2e2c1e0e2abeb" + sha256: bca87b0165ffd7cdb9cad8edd22d18d2201e886d9a9f19b4fb3452ea7df3a72a url: "https://pub.dev" source: hosted - version: "2.2.9" + version: "2.2.6" path_provider_foundation: dependency: transitive description: @@ -195,10 +203,10 @@ packages: dependency: transitive description: name: path_provider_windows - sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 + sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170" url: "https://pub.dev" source: hosted - version: "2.3.0" + version: "2.2.1" platform: dependency: transitive description: @@ -272,10 +280,18 @@ packages: dependency: transitive description: name: web - sha256: d43c1d6b787bf0afad444700ae7f4db8827f701bc61c255ac8d328c6f4d52062 + sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" + url: "https://pub.dev" + source: hosted + version: "0.5.1" + win32: + dependency: transitive + description: + name: win32 + sha256: a79dbe579cb51ecd6d30b17e0cae4e0ea15e2c0e66f69ad4198f22a6789e94f4 url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "5.5.1" xdg_directories: dependency: transitive description: @@ -286,4 +302,4 @@ packages: version: "1.0.4" sdks: dart: ">=3.4.0 <4.0.0" - flutter: ">=3.24.0" + flutter: ">=3.22.0" diff --git a/pubspec.yaml b/pubspec.yaml index f18a07a..215acfd 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,6 +15,7 @@ dependencies: flutter_localizations: sdk: flutter flutter_staggered_grid_view: ^0.7.0 + font_awesome_flutter: ^10.7.0 go_router: ^12.1.0 google_fonts: ^6.2.0 intl: ^0.19.0 diff --git a/widgetbook/.metadata b/widgetbook/.metadata index 56b8ab2..6e13c77 100644 --- a/widgetbook/.metadata +++ b/widgetbook/.metadata @@ -4,7 +4,7 @@ # This file should be version controlled and should not be manually edited. version: - revision: "ead455963c12b453cdb2358cad34969c76daf180" + revision: "a14f74ff3a1cbd521163c5f03d68113d50af93d3" channel: "stable" project_type: app @@ -13,11 +13,11 @@ project_type: app migration: platforms: - platform: root - create_revision: ead455963c12b453cdb2358cad34969c76daf180 - base_revision: ead455963c12b453cdb2358cad34969c76daf180 - - platform: web - create_revision: ead455963c12b453cdb2358cad34969c76daf180 - base_revision: ead455963c12b453cdb2358cad34969c76daf180 + create_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3 + base_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3 + - platform: macos + create_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3 + base_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3 # User provided section diff --git a/widgetbook/README.md b/widgetbook/README.md new file mode 100644 index 0000000..5122f24 --- /dev/null +++ b/widgetbook/README.md @@ -0,0 +1,16 @@ +# widgetbook + +A new Flutter project. + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) + +For help getting started with Flutter development, view the +[online documentation](https://docs.flutter.dev/), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/widgetbook/lib/basket/empty_basket_placeholder.dart b/widgetbook/lib/basket/empty_basket_placeholder.dart deleted file mode 100644 index 1301a78..0000000 --- a/widgetbook/lib/basket/empty_basket_placeholder.dart +++ /dev/null @@ -1,13 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:groceries_app/basket/basket.dart'; -import 'package:widgetbook_annotation/widgetbook_annotation.dart'; - -@UseCase( - name: 'Default', - type: EmptyBasketPlaceholder, - designLink: - 'https://www.figma.com/file/EXuEpwiyksLAejYX1qr1v4/Demo-App-featuring-variables?type=design&node-id=112-7193&mode=dev', -) -Widget buildEmptyBasketPlaceholderUseCase(BuildContext context) { - return const EmptyBasketPlaceholder(); -} diff --git a/widgetbook/lib/basket/quantity.dart b/widgetbook/lib/basket/quantity.dart deleted file mode 100644 index 442294c..0000000 --- a/widgetbook/lib/basket/quantity.dart +++ /dev/null @@ -1,20 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:groceries_app/basket/basket.dart'; -import 'package:widgetbook/widgetbook.dart'; -import 'package:widgetbook_annotation/widgetbook_annotation.dart'; - -@UseCase( - name: 'Default', - type: Quantity, - designLink: - 'https://www.figma.com/file/EXuEpwiyksLAejYX1qr1v4/Demo-App-featuring-variables?type=design&node-id=74-1996&mode=dev', -) -Widget buildQuantityUseCase(BuildContext context) { - return Quantity( - value: context.knobs.int.slider( - label: 'Value', - initialValue: 2, - max: 1000, - ), - ); -} diff --git a/widgetbook/lib/basket/summary.dart b/widgetbook/lib/basket/summary.dart deleted file mode 100644 index 078c3f7..0000000 --- a/widgetbook/lib/basket/summary.dart +++ /dev/null @@ -1,28 +0,0 @@ -import 'package:flutter/widgets.dart'; -import 'package:groceries_app/basket/basket.dart'; -import 'package:widgetbook/widgetbook.dart'; -import 'package:widgetbook_annotation/widgetbook_annotation.dart'; - -@UseCase( - name: 'Default', - type: Summary, - designLink: - 'https://www.figma.com/file/EXuEpwiyksLAejYX1qr1v4/Demo-App-featuring-variables?type=design&node-id=112-6510&mode=dev', -) -Widget buildSummaryUseCase(BuildContext context) { - return Padding( - padding: const EdgeInsets.all(8), - child: Summary( - subTotal: context.knobs.double.slider( - label: 'Subtotal', - initialValue: 2, - max: 1000, - ), - delivery: context.knobs.double.slider( - label: 'Delivery', - initialValue: 2, - max: 10, - ), - ), - ); -} diff --git a/widgetbook/lib/core/app_bar.dart b/widgetbook/lib/core/app_bar.dart deleted file mode 100644 index 1d572f0..0000000 --- a/widgetbook/lib/core/app_bar.dart +++ /dev/null @@ -1,23 +0,0 @@ -import 'package:flutter/widgets.dart'; -import 'package:groceries_app/core/core.dart'; -import 'package:widgetbook/widgetbook.dart'; -import 'package:widgetbook_annotation/widgetbook_annotation.dart'; - -@UseCase( - name: 'Default', - type: AppBar, - designLink: - 'https://www.figma.com/file/EXuEpwiyksLAejYX1qr1v4/Demo-App-featuring-variables?type=design&node-id=277-3056&mode=design&t=xEVe42MecBrfe39j-4', -) -Widget appBarUseCase(BuildContext context) { - return AppBar( - title: context.knobs.string( - label: 'Title', - initialValue: 'Groceries App', - ), - basketSize: context.knobs.int.input( - label: 'Basket Size', - initialValue: 1, - ), - ); -} diff --git a/widgetbook/lib/core/app_icon.dart b/widgetbook/lib/core/app_icon.dart deleted file mode 100644 index d44af1c..0000000 --- a/widgetbook/lib/core/app_icon.dart +++ /dev/null @@ -1,15 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:groceries_app/core/core.dart'; -import 'package:widgetbook_annotation/widgetbook_annotation.dart'; - -@UseCase( - name: 'Default', - type: AppIcon, - designLink: - 'https://www.figma.com/file/EXuEpwiyksLAejYX1qr1v4/Demo-App-featuring-variables?type=design&node-id=112-1457&mode=dev', -) -Widget buildAppIconUseCase(BuildContext context) { - return const AppIcon( - icon: Icons.shopping_bag_outlined, - ); -} diff --git a/widgetbook/lib/core/badge.dart b/widgetbook/lib/core/badge.dart deleted file mode 100644 index 6b3fcfb..0000000 --- a/widgetbook/lib/core/badge.dart +++ /dev/null @@ -1,18 +0,0 @@ -import 'package:flutter/widgets.dart'; -import 'package:groceries_app/core/core.dart'; -import 'package:widgetbook/widgetbook.dart'; -import 'package:widgetbook_annotation/widgetbook_annotation.dart'; - -@UseCase( - name: 'Default', - type: Badge, - designLink: - 'https://www.figma.com/file/EXuEpwiyksLAejYX1qr1v4/Demo-App-featuring-variables?type=design&node-id=112-3469&mode=dev', -) -Widget badge(BuildContext context) { - return Badge( - number: context.knobs.int.input( - label: 'Number', - ), - ); -} diff --git a/widgetbook/lib/core/card.dart b/widgetbook/lib/core/card.dart deleted file mode 100644 index e65e61a..0000000 --- a/widgetbook/lib/core/card.dart +++ /dev/null @@ -1,22 +0,0 @@ -import 'package:flutter/widgets.dart'; -import 'package:groceries_app/core/core.dart'; -import 'package:groceries_app/theme/app_theme.dart'; -import 'package:widgetbook_annotation/widgetbook_annotation.dart'; - -@UseCase( - name: 'Default', - type: Card, - designLink: - 'https://www.figma.com/file/EXuEpwiyksLAejYX1qr1v4/Demo-App-featuring-variables?type=design&node-id=74-2014&mode=dev', -) -Widget card(BuildContext context) { - return Card( - child: Padding( - padding: const EdgeInsets.all(8), - child: Text( - 'This is some text', - style: AppTheme.of(context).typography.bodyMedium16, - ), - ), - ); -} diff --git a/widgetbook/lib/core/primary_button.dart b/widgetbook/lib/core/primary_button.dart deleted file mode 100644 index 1499985..0000000 --- a/widgetbook/lib/core/primary_button.dart +++ /dev/null @@ -1,22 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:groceries_app/core/core.dart'; -import 'package:widgetbook/widgetbook.dart'; -import 'package:widgetbook_annotation/widgetbook_annotation.dart'; - -@UseCase( - name: 'Default', - type: PrimaryButton, - designLink: - 'https://www.figma.com/file/EXuEpwiyksLAejYX1qr1v4/Demo-App-featuring-variables?type=design&node-id=86-1012&mode=dev', -) -Widget buildContinueButtonUseCase(BuildContext context) { - return Padding( - padding: const EdgeInsets.all(8), - child: PrimaryButton( - content: context.knobs.string( - label: 'Content', - initialValue: 'Continue to Shipping', - ), - ), - ); -} diff --git a/widgetbook/lib/core/quantity_button.dart b/widgetbook/lib/core/quantity_button.dart deleted file mode 100644 index 295b788..0000000 --- a/widgetbook/lib/core/quantity_button.dart +++ /dev/null @@ -1,23 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:groceries_app/core/core.dart'; -import 'package:widgetbook_annotation/widgetbook_annotation.dart'; - -@UseCase( - name: 'Add', - type: QuantityButton, - designLink: - 'https://www.figma.com/file/EXuEpwiyksLAejYX1qr1v4/Demo-App-featuring-variables?type=design&node-id=74-1996&mode=dev', -) -Widget buildQuantityButtonAddUseCase(BuildContext context) { - return const QuantityButton.add(); -} - -@UseCase( - name: 'Remove', - type: QuantityButton, - designLink: - 'https://www.figma.com/file/EXuEpwiyksLAejYX1qr1v4/Demo-App-featuring-variables?type=design&node-id=74-1996&mode=dev', -) -Widget buildQuantityButtonRemoveUseCase(BuildContext context) { - return const QuantityButton.remove(); -} diff --git a/widgetbook/lib/basket/basket_screen.dart b/widgetbook/lib/features/basket/views/basket_view.dart similarity index 57% rename from widgetbook/lib/basket/basket_screen.dart rename to widgetbook/lib/features/basket/views/basket_view.dart index 9f50490..74c2b00 100644 --- a/widgetbook/lib/basket/basket_screen.dart +++ b/widgetbook/lib/features/basket/views/basket_view.dart @@ -1,16 +1,30 @@ import 'package:flutter/widgets.dart'; -import 'package:groceries_app/basket/basket.dart'; +import 'package:groceries_app/features/features.dart'; import 'package:groceries_app/repositories/data_store.dart'; import 'package:widgetbook/widgetbook.dart'; import 'package:widgetbook_annotation/widgetbook_annotation.dart'; @UseCase( - name: 'Filled', - type: BasketScreen, + name: 'Empty', + type: BasketView, + designLink: + "https://www.figma.com/design/TQ3x8ohiB7XfUKSYeFVb7v/FlutterCon-'24?node-id=7271-82082&t=Jj5fjVUaGAsV0QSp-4", +) +Widget buildBasketViewEmptyUseCase(BuildContext context) { + return const BasketView( + basket: {}, + delivery: 0, + subTotal: 0, + ); +} + +@UseCase( + name: 'Non-empty', + type: BasketView, designLink: - 'https://www.figma.com/file/EXuEpwiyksLAejYX1qr1v4/Demo-App-featuring-variables?type=design&node-id=60-1089&mode=dev', + "https://www.figma.com/design/TQ3x8ohiB7XfUKSYeFVb7v/FlutterCon-'24?node-id=7271-82082&t=Jj5fjVUaGAsV0QSp-4", ) -Widget buildFilledBasketScreenUseCase(BuildContext context) { +Widget buildBasketViewUseCase(BuildContext context) { final fruit_1 = DataStore.fruits[0]; final fruit_2 = DataStore.fruits[1]; @@ -37,28 +51,7 @@ Widget buildFilledBasketScreenUseCase(BuildContext context) { builder: (context) { final basketState = BasketState.of(context); - return BasketScreen( - basket: basketState.store, - delivery: basketState.delivery, - subTotal: basketState.subTotal, - ); - }, - ), - ); -} - -@UseCase( - name: 'Empty', - type: BasketScreen, - designLink: - 'https://www.figma.com/file/EXuEpwiyksLAejYX1qr1v4/Demo-App-featuring-variables?type=design&node-id=74-2186&mode=dev', -) -Widget buildEmptyBasketScreenUseCase(BuildContext context) { - return BasketScope( - child: Builder( - builder: (context) { - final basketState = BasketState.of(context); - return BasketScreen( + return BasketView( basket: basketState.store, delivery: basketState.delivery, subTotal: basketState.subTotal, diff --git a/widgetbook/lib/basket/basket_card.dart b/widgetbook/lib/features/basket/widgets/basket_card.dart similarity index 69% rename from widgetbook/lib/basket/basket_card.dart rename to widgetbook/lib/features/basket/widgets/basket_card.dart index 09fe8e5..36ce56d 100644 --- a/widgetbook/lib/basket/basket_card.dart +++ b/widgetbook/lib/features/basket/widgets/basket_card.dart @@ -1,5 +1,5 @@ -import 'package:flutter/material.dart'; -import 'package:groceries_app/basket/basket.dart'; +import 'package:flutter/widgets.dart'; +import 'package:groceries_app/features/features.dart'; import 'package:groceries_app/repositories/data_store.dart'; import 'package:widgetbook/widgetbook.dart'; import 'package:widgetbook_annotation/widgetbook_annotation.dart'; @@ -8,7 +8,7 @@ import 'package:widgetbook_annotation/widgetbook_annotation.dart'; name: 'Default', type: BasketCard, designLink: - 'https://www.figma.com/file/EXuEpwiyksLAejYX1qr1v4/Demo-App-featuring-variables?type=design&node-id=74-2014&mode=dev', + "https://www.figma.com/design/TQ3x8ohiB7XfUKSYeFVb7v/FlutterCon-'24?node-id=7271-82082&t=Jj5fjVUaGAsV0QSp-4", ) Widget buildBasketCardUseCase(BuildContext context) { return Padding( @@ -22,6 +22,8 @@ Widget buildBasketCardUseCase(BuildContext context) { count: context.knobs.int.input( label: 'Count', ), + onFruitAdded: () {}, + onFruitRemoved: () => {}, ), ); } diff --git a/widgetbook/lib/features/basket/widgets/cost_item_row.dart b/widgetbook/lib/features/basket/widgets/cost_item_row.dart new file mode 100644 index 0000000..b71b4b0 --- /dev/null +++ b/widgetbook/lib/features/basket/widgets/cost_item_row.dart @@ -0,0 +1,28 @@ +import 'package:flutter/widgets.dart'; +import 'package:groceries_app/features/features.dart'; +import 'package:widgetbook/widgetbook.dart'; +import 'package:widgetbook_annotation/widgetbook_annotation.dart'; + +@UseCase( + name: 'Default', + type: CostItemRow, + designLink: + "https://www.figma.com/design/TQ3x8ohiB7XfUKSYeFVb7v/FlutterCon-'24?node-id=7271-82082&t=Jj5fjVUaGAsV0QSp-4", +) +Widget buildAppBarUseCase(BuildContext context) { + return Padding( + padding: const EdgeInsets.all(8), + child: CostItemRow( + label: context.knobs.string( + label: 'label', + initialValue: 'Delivery', + ), + cost: context.knobs.double.slider( + label: 'Cost', + initialValue: 5, + max: 10000, + divisions: 100, + ), + ), + ); +} diff --git a/widgetbook/lib/features/basket/widgets/summary.dart b/widgetbook/lib/features/basket/widgets/summary.dart new file mode 100644 index 0000000..9424050 --- /dev/null +++ b/widgetbook/lib/features/basket/widgets/summary.dart @@ -0,0 +1,21 @@ +import 'package:flutter/widgets.dart'; +import 'package:groceries_app/features/features.dart'; +import 'package:widgetbook/widgetbook.dart'; +import 'package:widgetbook_annotation/widgetbook_annotation.dart'; + +@UseCase( + name: 'Default', + type: Summary, + designLink: + "https://www.figma.com/design/TQ3x8ohiB7XfUKSYeFVb7v/FlutterCon-'24?node-id=7271-82082&t=Jj5fjVUaGAsV0QSp-4", +) +Widget buildSummaryUseCase(BuildContext context) { + return Padding( + padding: const EdgeInsets.all(8), + child: Summary( + subTotal: + context.knobs.double.slider(label: 'subTotal', initialValue: 10), + delivery: context.knobs.double.slider(label: 'delivery', initialValue: 5), + ), + ); +} diff --git a/widgetbook/lib/features/utility/placeholder.dart b/widgetbook/lib/features/utility/placeholder.dart new file mode 100644 index 0000000..467e3b1 --- /dev/null +++ b/widgetbook/lib/features/utility/placeholder.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; +import 'package:widgetbook_annotation/widgetbook_annotation.dart'; + +class Placeholder extends StatelessWidget { + const Placeholder({super.key}); + + @override + Widget build(BuildContext context) { + return Container( + color: const Color(0xFFD9D9D9), + ); + } +} + +@UseCase( + name: 'Default', + type: Placeholder, + designLink: + "https://www.figma.com/design/TQ3x8ohiB7XfUKSYeFVb7v/FlutterCon-'24?node-id=6802-3280&t=OwNskhPgyoe2Fkcc-4", +) +Widget placeholderUseCase(BuildContext context) { + return const Placeholder(); +} diff --git a/widgetbook/lib/home/add_basket_button.dart b/widgetbook/lib/home/add_basket_button.dart deleted file mode 100644 index 5a478e1..0000000 --- a/widgetbook/lib/home/add_basket_button.dart +++ /dev/null @@ -1,15 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:groceries_app/home/home.dart'; -import 'package:widgetbook_annotation/widgetbook_annotation.dart'; - -@UseCase( - name: 'Default', - type: AddBasketButton, - designLink: - 'https://www.figma.com/file/EXuEpwiyksLAejYX1qr1v4/Demo-App-featuring-variables?type=design&node-id=112-2401&mode=dev', -) -Widget buildIconButtonUseCase(BuildContext context) { - return const AddBasketButton( - icon: Icons.add, - ); -} diff --git a/widgetbook/lib/home/fruit_card.dart b/widgetbook/lib/home/fruit_card.dart deleted file mode 100644 index 2fc7b63..0000000 --- a/widgetbook/lib/home/fruit_card.dart +++ /dev/null @@ -1,19 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:groceries_app/home/home.dart'; -import 'package:groceries_app/repositories/data_store.dart'; -import 'package:widgetbook_annotation/widgetbook_annotation.dart'; - -@UseCase( - name: 'Default', - type: FruitCard, - designLink: - 'https://www.figma.com/file/EXuEpwiyksLAejYX1qr1v4/Demo-App-featuring-variables?type=design&node-id=60-3200&mode=dev', -) -Widget fruitCard(BuildContext context) { - return Padding( - padding: const EdgeInsets.all(8), - child: FruitCard( - fruit: DataStore.fruits[0], - ), - ); -} diff --git a/widgetbook/lib/home/home_screen.dart b/widgetbook/lib/home/home_screen.dart deleted file mode 100644 index d1ee1fc..0000000 --- a/widgetbook/lib/home/home_screen.dart +++ /dev/null @@ -1,19 +0,0 @@ -import 'package:flutter/widgets.dart'; -import 'package:groceries_app/basket/basket.dart'; -import 'package:groceries_app/home/home.dart'; -import 'package:groceries_app/repositories/data_store.dart'; -import 'package:widgetbook_annotation/widgetbook_annotation.dart'; - -@UseCase( - name: 'Home', - type: HomeScreen, - designLink: - 'https://www.figma.com/file/EXuEpwiyksLAejYX1qr1v4/Demo-App-featuring-variables?type=design&node-id=70-891&mode=dev', -) -Widget buildHomeUseCase(BuildContext context) { - return BasketScope( - child: const HomeScreen( - fruits: DataStore.fruits, - ), - ); -} diff --git a/widgetbook/lib/main.dart b/widgetbook/lib/main.dart index 5aa0b0e..489068c 100644 --- a/widgetbook/lib/main.dart +++ b/widgetbook/lib/main.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:groceries_app/l10n/app_localizations.dart'; -import 'package:groceries_app/theme/theme.dart'; +import 'package:groceries_app/ui/ui.dart'; import 'package:widgetbook/widgetbook.dart'; import 'package:widgetbook_annotation/widgetbook_annotation.dart'; @@ -43,17 +43,17 @@ class WidgetbookApp extends StatelessWidget { themes: [ WidgetbookTheme( name: 'Light', - data: lightTheme, + data: AppThemeData.light, ), WidgetbookTheme( name: 'Dark', - data: darkTheme, + data: AppThemeData.dark, ), ], themeBuilder: (context, theme, child) => ColoredBox( - color: theme.surface.primary, + color: theme.background.primary, child: DefaultTextStyle( - style: theme.typography.bodyMedium16, + style: theme.typography.bodyMedium, child: AppTheme( data: theme, child: child, diff --git a/widgetbook/lib/theme/group.dart b/widgetbook/lib/theme/group.dart deleted file mode 100644 index 17e626a..0000000 --- a/widgetbook/lib/theme/group.dart +++ /dev/null @@ -1,34 +0,0 @@ -import 'package:flutter/widgets.dart'; - -class WidgetbookGroup extends StatelessWidget { - const WidgetbookGroup({ - super.key, - required this.label, - required this.children, - }); - - final String label; - final List children; - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.all(16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(label), - const SizedBox( - height: 16, - ), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: children, - ), - ), - ], - ), - ); - } -} diff --git a/widgetbook/lib/theme/spacing.dart b/widgetbook/lib/theme/spacing.dart deleted file mode 100644 index 38c329b..0000000 --- a/widgetbook/lib/theme/spacing.dart +++ /dev/null @@ -1,170 +0,0 @@ -import 'dart:math'; - -import 'package:flutter/material.dart'; -import 'package:groceries_app/theme/theme.dart'; -import 'package:widgetbook_annotation/widgetbook_annotation.dart'; - -import 'group.dart'; - -@UseCase( - name: 'Spacing', - type: SpacingThemeData, - path: '[Theme]', -) -Widget buildWidgetbookSpacingUseCase(BuildContext context) { - return WidgetbookGroup( - label: 'Spacing', - children: [ - WidgetbookSpacing( - label: 'none', - spacing: AppTheme.of(context).spacing.none, - ), - WidgetbookSpacing( - label: 'xs', - spacing: AppTheme.of(context).spacing.extraSmall, - ), - WidgetbookSpacing( - label: 'small', - spacing: AppTheme.of(context).spacing.small, - ), - WidgetbookSpacing( - label: 'medium', - spacing: AppTheme.of(context).spacing.medium, - ), - WidgetbookSpacing( - label: 'large', - spacing: AppTheme.of(context).spacing.large, - ), - WidgetbookSpacing( - label: 'xl', - spacing: AppTheme.of(context).spacing.extraLarge, - ), - WidgetbookSpacing( - label: '2xl', - spacing: AppTheme.of(context).spacing.extraExtraLarge, - ), - WidgetbookSpacing( - label: '3xl', - spacing: AppTheme.of(context).spacing.extraExtraExtraLarge, - ), - ], - ); -} - -class LineContainer extends StatelessWidget { - const LineContainer({ - super.key, - this.lineWidth = 2.0, - this.lineSpacing = 5.0, - }); - - final double lineWidth; - final double lineSpacing; - - @override - Widget build(BuildContext context) { - return ClipRect( - child: CustomPaint( - painter: LinePainter(lineWidth: lineWidth, lineSpacing: lineSpacing), - ), - ); - } -} - -class LinePainter extends CustomPainter { - LinePainter({ - required this.lineWidth, - required this.lineSpacing, - }); - final double lineWidth; - final double lineSpacing; - - @override - void paint(Canvas canvas, Size size) { - final paint = Paint() - ..color = const Color(0xFFE93EB0) - ..strokeWidth = lineWidth - ..style = PaintingStyle.stroke; - - final path = Path(); - - for (var x = -max(size.width, size.height); - x <= 2 * max(size.width, size.height); - x += lineSpacing) { - path - ..moveTo(x, 0) - ..lineTo(x + size.height, size.height); - } - - canvas.drawPath(path, paint); - } - - @override - bool shouldRepaint(covariant CustomPainter oldDelegate) { - return true; - } -} - -class WidgetbookSpacing extends StatelessWidget { - const WidgetbookSpacing({ - required this.spacing, - required this.label, - super.key, - this.color, - }); - - final String label; - final double spacing; - final Color? color; - - @override - Widget build(BuildContext context) { - final theColor = color ?? AppTheme.of(context).surface.secondary; - return Row( - children: [ - Expanded( - child: Text(label), - ), - const SizedBox( - width: 24, - ), - Expanded( - child: Text( - spacing.toString(), - ), - ), - const SizedBox( - width: 24, - ), - Expanded( - flex: 2, - child: Row( - children: [ - Container( - decoration: BoxDecoration( - color: theColor, - borderRadius: BorderRadius.circular(4), - ), - width: 24, - height: 24, - ), - SizedBox( - width: spacing, - height: 24, - child: const LineContainer(), - ), - Container( - decoration: BoxDecoration( - color: theColor, - borderRadius: BorderRadius.circular(4), - ), - width: 24, - height: 24, - ), - ], - ), - ), - ], - ); - } -} diff --git a/widgetbook/lib/theme/surface.dart b/widgetbook/lib/theme/surface.dart deleted file mode 100644 index 3cf3b33..0000000 --- a/widgetbook/lib/theme/surface.dart +++ /dev/null @@ -1,78 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:groceries_app/theme/theme.dart'; -import 'package:widgetbook_annotation/widgetbook_annotation.dart'; -import 'group.dart'; - -@UseCase( - name: 'Surface', - type: SurfaceThemeData, - path: '[Theme]', -) -Widget buildColorUseCase(BuildContext context) { - return WidgetbookGroup( - label: 'Surface', - children: [ - ColorLabel( - label: 'Primary', - color: AppTheme.of(context).surface.primary, - ), - ColorLabel( - label: 'Secondary', - color: AppTheme.of(context).surface.secondary, - ), - ColorLabel( - label: 'Invert', - color: AppTheme.of(context).surface.invert, - ), - ColorLabel( - label: 'Light', - color: AppTheme.of(context).surface.light, - ), - ColorLabel( - label: 'Tertiary', - color: AppTheme.of(context).surface.tertiary, - ), - ColorLabel( - label: 'Brand', - color: AppTheme.of(context).surface.brand, - ), - ColorLabel( - label: 'Feature', - color: AppTheme.of(context).surface.feature, - ), - ], - ); -} - -class ColorLabel extends StatelessWidget { - const ColorLabel({ - super.key, - required this.label, - required this.color, - }); - - final String label; - final Color color; - - @override - Widget build(BuildContext context) { - return Row( - children: [ - Container( - width: 24, - height: 24, - decoration: BoxDecoration( - color: color, - borderRadius: BorderRadius.circular(4), - border: Border.all( - color: Colors.white, - width: 2, - ), - ), - ), - const SizedBox(width: 16), - Text(label), - ], - ); - } -} diff --git a/widgetbook/lib/theme/typography.dart b/widgetbook/lib/theme/typography.dart deleted file mode 100644 index ff566ed..0000000 --- a/widgetbook/lib/theme/typography.dart +++ /dev/null @@ -1,33 +0,0 @@ -import 'package:flutter/widgets.dart'; -import 'package:groceries_app/theme/theme.dart'; -import 'package:groceries_app/theme/typography_theme_data.dart'; -import 'package:widgetbook_annotation/widgetbook_annotation.dart'; - -import 'group.dart'; - -@UseCase( - name: 'Typography', - type: TypographyThemeData, - path: '[Theme]', -) -Widget buildTypographyThemeDataUseCase(BuildContext context) { - const data = 'The lazy dog jumped over the quick brown fox'; - - return WidgetbookGroup( - label: 'Typography', - children: [ - Text(data, style: AppTheme.of(context).typography.displayRegular32), - Text(data, style: AppTheme.of(context).typography.headingSemibold20), - Text(data, style: AppTheme.of(context).typography.headingMedium24), - Text(data, style: AppTheme.of(context).typography.bodyRegular12), - Text(data, style: AppTheme.of(context).typography.bodyRegular14), - Text(data, style: AppTheme.of(context).typography.bodyMedium16), - Text(data, style: AppTheme.of(context).typography.bodySemiBold16), - Text(data, style: AppTheme.of(context).typography.labelMedium11), - Text(data, style: AppTheme.of(context).typography.labelMedium14), - Text(data, style: AppTheme.of(context).typography.subheadingRegular16), - Text(data, style: AppTheme.of(context).typography.subheadingMedium20), - Text(data, style: AppTheme.of(context).typography.logoSemiBold20), - ], - ); -} diff --git a/widgetbook/lib/ui/foundation/color.dart b/widgetbook/lib/ui/foundation/color.dart new file mode 100644 index 0000000..1c4376f --- /dev/null +++ b/widgetbook/lib/ui/foundation/color.dart @@ -0,0 +1,107 @@ +import 'package:flutter/material.dart'; +import 'package:groceries_app/ui/ui.dart'; +import 'package:widgetbook_annotation/widgetbook_annotation.dart'; + +class ColorWidget extends StatelessWidget { + const ColorWidget({ + super.key, + required this.color, + required this.weight, + }); + + final Color color; + final int weight; + + @override + Widget build(BuildContext context) { + return Container( + height: 50, + color: color, + child: Center( + child: Text( + weight.toString(), + style: TextStyle( + color: (weight > 400) ? Colors.white : Colors.black, + ), + ), + ), + ); + } +} + +class ColorSwatchWidget extends StatelessWidget { + const ColorSwatchWidget({ + super.key, + required this.name, + required this.color, + }); + + final String name; + final DesignSystemColor color; + + @override + Widget build(BuildContext context) { + final colorMap = { + 100: color.shade100, + 200: color.shade200, + 300: color.shade300, + 400: color.shade400, + 500: color.shade500, + 600: color.shade600, + 700: color.shade700, + 800: color.shade800, + 900: color.shade900, + }; + + return Padding( + padding: const EdgeInsets.all(8), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + name, + ), + const SizedBox( + height: 8, + ), + ClipRRect( + borderRadius: BorderRadius.circular(8), + child: Row( + children: [ + for (final entry in colorMap.entries) + Expanded( + child: ColorWidget( + color: entry.value, + weight: entry.key, + ), + ), + ], + ), + ), + ], + ), + ); + } +} + +@UseCase( + name: 'Default', + type: DesignSystemColor, + designLink: + 'https://www.figma.com/design/HsANkdhbsCNTkXBzNJRNLD/Groceries-Demo?node-id=7340-24552&t=uJW8KKcBrCOkO7C7-4', +) +Widget buildDesignSystemColorUseCase(BuildContext context) { + return const Column( + children: [ + ColorSwatchWidget(name: 'Primary', color: DesignSystemColor.primary), + SizedBox( + height: 16, + ), + ColorSwatchWidget(name: 'Brand', color: DesignSystemColor.brand), + SizedBox( + height: 16, + ), + ColorSwatchWidget(name: 'Grey', color: DesignSystemColor.grey), + ], + ); +} diff --git a/widgetbook/lib/ui/foundation/radius.dart b/widgetbook/lib/ui/foundation/radius.dart new file mode 100644 index 0000000..b414952 --- /dev/null +++ b/widgetbook/lib/ui/foundation/radius.dart @@ -0,0 +1,94 @@ +import 'package:flutter/material.dart'; +import 'package:groceries_app/ui/ui.dart'; +import 'package:widgetbook_annotation/widgetbook_annotation.dart'; + +class RadiusWidget extends StatelessWidget { + const RadiusWidget({ + super.key, + required this.name, + required this.radius, + }); + + final double radius; + final String name; + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.all(8), + child: Row( + children: [ + SizedBox( + width: 100, + child: Text( + name, + ), + ), + Container( + height: 80, + width: 80, + decoration: BoxDecoration( + color: DesignSystemColor.brand.shade400.withOpacity(0.15), + borderRadius: BorderRadius.circular(radius), + border: Border.all( + width: 2, + color: DesignSystemColor.brand.shade400, + ), + ), + ), + const SizedBox( + width: 16, + ), + Text('${radius.toStringAsFixed(0)}px'), + ], + ), + ); + } +} + +@UseCase( + name: 'Default', + type: DesignSystemRadius, + designLink: + 'https://www.figma.com/design/HsANkdhbsCNTkXBzNJRNLD/Groceries-Demo?node-id=7340-24769&t=uJW8KKcBrCOkO7C7-4', +) +Widget buildDesignSystemRadiusUseCase(BuildContext context) { + return const SingleChildScrollView( + child: Column( + children: [ + RadiusWidget( + name: 'none', + radius: DesignSystemRadius.none, + ), + RadiusWidget( + name: 'xs', + radius: DesignSystemRadius.xs, + ), + RadiusWidget( + name: 'sm', + radius: DesignSystemRadius.sm, + ), + RadiusWidget( + name: 'md', + radius: DesignSystemRadius.md, + ), + RadiusWidget( + name: 'lg', + radius: DesignSystemRadius.lg, + ), + RadiusWidget( + name: 'xl', + radius: DesignSystemRadius.xl, + ), + RadiusWidget( + name: 'xxl', + radius: DesignSystemRadius.xxl, + ), + RadiusWidget( + name: 'full', + radius: DesignSystemRadius.full, + ), + ], + ), + ); +} diff --git a/widgetbook/lib/ui/foundation/spacing.dart b/widgetbook/lib/ui/foundation/spacing.dart new file mode 100644 index 0000000..aa09bee --- /dev/null +++ b/widgetbook/lib/ui/foundation/spacing.dart @@ -0,0 +1,90 @@ +import 'package:flutter/material.dart'; +import 'package:groceries_app/ui/ui.dart'; +import 'package:widgetbook_annotation/widgetbook_annotation.dart'; + +class SpacingWidget extends StatelessWidget { + const SpacingWidget({ + super.key, + required this.spacing, + required this.name, + }); + + final double spacing; + final String name; + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.all(8), + child: Row( + children: [ + SizedBox( + width: 100, + child: Text( + name, + ), + ), + Container( + height: 28, + width: spacing, + decoration: BoxDecoration( + color: DesignSystemColor.brand.shade400, + borderRadius: BorderRadius.circular(2), + ), + ), + const SizedBox( + width: 16, + ), + Text('${spacing.toStringAsFixed(0)}px'), + ], + ), + ); + } +} + +@UseCase( + name: 'Default', + type: DesignSystemSpacing, + designLink: + 'https://www.figma.com/design/HsANkdhbsCNTkXBzNJRNLD/Groceries-Demo?node-id=7340-24767&t=uJW8KKcBrCOkO7C7-4', +) +Widget buildDesignSystemSpacingUseCase(BuildContext context) { + return const SingleChildScrollView( + child: Column( + children: [ + SpacingWidget( + name: 'none', + spacing: DesignSystemSpacing.zero, + ), + SpacingWidget( + name: 'xxs', + spacing: DesignSystemSpacing.xxs, + ), + SpacingWidget( + name: 'xs', + spacing: DesignSystemSpacing.xs, + ), + SpacingWidget( + name: 'small', + spacing: DesignSystemSpacing.sm, + ), + SpacingWidget( + name: 'medium', + spacing: DesignSystemSpacing.m, + ), + SpacingWidget( + name: 'large', + spacing: DesignSystemSpacing.l, + ), + SpacingWidget( + name: 'xl', + spacing: DesignSystemSpacing.xl, + ), + SpacingWidget( + name: 'xxl', + spacing: DesignSystemSpacing.xxl, + ), + ], + ), + ); +} diff --git a/widgetbook/lib/ui/foundation/typography.dart b/widgetbook/lib/ui/foundation/typography.dart new file mode 100644 index 0000000..9a25761 --- /dev/null +++ b/widgetbook/lib/ui/foundation/typography.dart @@ -0,0 +1,53 @@ +import 'package:flutter/material.dart'; +import 'package:groceries_app/ui/ui.dart'; +import 'package:widgetbook_annotation/widgetbook_annotation.dart'; + +class TextWidget extends StatelessWidget { + const TextWidget({ + super.key, + required this.name, + required this.style, + }); + + final String name; + final TextStyle style; + + @override + Widget build(BuildContext context) { + return Text( + name, + style: style, + ); + } +} + +@UseCase( + name: 'Default', + type: DesignSystemTextStyles, + designLink: + 'https://www.figma.com/design/HsANkdhbsCNTkXBzNJRNLD/Groceries-Demo?node-id=7225-2964&t=N6qwmLP7MP59ClWB-4', +) +Widget buildDesignSystemTextStylesUseCase(BuildContext context) { + final styles = DesignSystemTextStyles(); + const spacing = 16.0; + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TextWidget(name: 'Display', style: styles.display), + const SizedBox(height: spacing), + TextWidget(name: 'Heading Large', style: styles.headingExtraLarge), + TextWidget(name: 'Heading Medium', style: styles.headingLarge), + const SizedBox(height: spacing), + TextWidget(name: 'Subheading Large', style: styles.subheadingLarge), + TextWidget(name: 'Subheading Small', style: styles.subheadingSmall), + const SizedBox(height: spacing), + TextWidget(name: 'Body Semi Bold', style: styles.bodySemiBold), + TextWidget(name: 'Body Medium', style: styles.bodyMedium), + TextWidget(name: 'Body Regular', style: styles.bodyRegular), + const SizedBox(height: spacing), + TextWidget(name: 'Body Small', style: styles.bodySmall), + const SizedBox(height: spacing), + TextWidget(name: 'Label', style: styles.label), + ], + ); +} diff --git a/widgetbook/lib/ui/widgets/app_bar.dart b/widgetbook/lib/ui/widgets/app_bar.dart new file mode 100644 index 0000000..8270039 --- /dev/null +++ b/widgetbook/lib/ui/widgets/app_bar.dart @@ -0,0 +1,19 @@ +import 'package:flutter/widgets.dart'; +import 'package:groceries_app/ui/ui.dart'; +import 'package:widgetbook/widgetbook.dart'; +import 'package:widgetbook_annotation/widgetbook_annotation.dart'; + +@UseCase( + name: 'Default', + type: AppBar, + designLink: + 'https://www.figma.com/design/HsANkdhbsCNTkXBzNJRNLD/Groceries-Demo?node-id=7271-82082&t=N6qwmLP7MP59ClWB-4', +) +Widget buildAppBarUseCase(BuildContext context) { + return AppBar( + title: context.knobs.string( + label: 'title', + initialValue: 'Title', + ), + ); +} diff --git a/widgetbook/lib/ui/widgets/bottom_navigation_bar.dart b/widgetbook/lib/ui/widgets/bottom_navigation_bar.dart new file mode 100644 index 0000000..e2d6924 --- /dev/null +++ b/widgetbook/lib/ui/widgets/bottom_navigation_bar.dart @@ -0,0 +1,13 @@ +import 'package:flutter/widgets.dart'; +import 'package:groceries_app/ui/ui.dart'; +import 'package:widgetbook_annotation/widgetbook_annotation.dart'; + +@UseCase( + name: 'Default', + type: BottomNavigationBar, + designLink: + 'https://www.figma.com/design/HsANkdhbsCNTkXBzNJRNLD/Groceries-Demo?node-id=7271-52117&t=N6qwmLP7MP59ClWB-4', +) +Widget buildBottomNavigationBarUseCase(BuildContext context) { + return const BottomNavigationBar(); +} diff --git a/widgetbook/lib/ui/widgets/card.dart b/widgetbook/lib/ui/widgets/card.dart new file mode 100644 index 0000000..4ac0346 --- /dev/null +++ b/widgetbook/lib/ui/widgets/card.dart @@ -0,0 +1,20 @@ +import 'package:flutter/widgets.dart'; +import 'package:groceries_app/ui/ui.dart'; +import 'package:widgetbook_annotation/widgetbook_annotation.dart'; + +@UseCase( + name: 'Default', + type: Card, + designLink: + 'https://www.figma.com/design/HsANkdhbsCNTkXBzNJRNLD/Groceries-Demo?node-id=7340-24498&t=N6qwmLP7MP59ClWB-4', +) +Widget buildCardUseCase(BuildContext context) { + return const Card( + child: Padding( + padding: EdgeInsets.all(8), + child: Text( + 'This is some text', + ), + ), + ); +} diff --git a/widgetbook/lib/ui/widgets/counter.dart b/widgetbook/lib/ui/widgets/counter.dart new file mode 100644 index 0000000..e4eeffb --- /dev/null +++ b/widgetbook/lib/ui/widgets/counter.dart @@ -0,0 +1,18 @@ +import 'package:flutter/widgets.dart'; +import 'package:groceries_app/ui/ui.dart'; +import 'package:widgetbook/widgetbook.dart'; +import 'package:widgetbook_annotation/widgetbook_annotation.dart'; + +@UseCase( + name: 'Default', + type: Counter, + designLink: + 'https://www.figma.com/design/HsANkdhbsCNTkXBzNJRNLD/Groceries-Demo?node-id=7284-8778&t=N6qwmLP7MP59ClWB-4', +) +Widget buildCounterCase(BuildContext context) { + return Counter( + value: context.knobs.int.input(label: 'value'), + onIncrement: () {}, + onDecrement: () {}, + ); +} diff --git a/widgetbook/lib/ui/widgets/divider.dart b/widgetbook/lib/ui/widgets/divider.dart new file mode 100644 index 0000000..6c1971f --- /dev/null +++ b/widgetbook/lib/ui/widgets/divider.dart @@ -0,0 +1,13 @@ +import 'package:flutter/widgets.dart'; +import 'package:groceries_app/ui/ui.dart'; +import 'package:widgetbook_annotation/widgetbook_annotation.dart'; + +@UseCase( + name: 'Default', + type: Divider, + designLink: + 'https://www.figma.com/design/HsANkdhbsCNTkXBzNJRNLD/Groceries-Demo?node-id=7271-23241&t=N6qwmLP7MP59ClWB-4', +) +Widget buildDividerUseCase(BuildContext context) { + return const Divider(); +} diff --git a/widgetbook/lib/ui/widgets/empty_state.dart b/widgetbook/lib/ui/widgets/empty_state.dart new file mode 100644 index 0000000..7670003 --- /dev/null +++ b/widgetbook/lib/ui/widgets/empty_state.dart @@ -0,0 +1,22 @@ +import 'package:flutter/widgets.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:groceries_app/ui/ui.dart'; +import 'package:widgetbook/widgetbook.dart'; +import 'package:widgetbook_annotation/widgetbook_annotation.dart'; + +@UseCase( + name: 'Default', + type: EmptyState, + designLink: + 'https://www.figma.com/design/HsANkdhbsCNTkXBzNJRNLD/Groceries-Demo?node-id=6809-5957&t=N6qwmLP7MP59ClWB-4', +) +Widget buildEmptyStateUseCase(BuildContext context) { + return EmptyState( + icon: FontAwesomeIcons.triangleExclamation, + title: context.knobs.string(label: 'title', initialValue: 'Title'), + message: context.knobs.string( + label: 'message', + initialValue: 'A message conveying the state of the product', + ), + ); +} diff --git a/widgetbook/lib/ui/widgets/icon.dart b/widgetbook/lib/ui/widgets/icon.dart new file mode 100644 index 0000000..347096e --- /dev/null +++ b/widgetbook/lib/ui/widgets/icon.dart @@ -0,0 +1,14 @@ +import 'package:flutter/widgets.dart' hide Icon; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:groceries_app/ui/ui.dart'; +import 'package:widgetbook_annotation/widgetbook_annotation.dart'; + +@UseCase( + name: 'Default', + type: Icon, +) +Widget buildIconUseCase(BuildContext context) { + return const Icon( + FontAwesomeIcons.user, + ); +} diff --git a/widgetbook/lib/ui/widgets/icon_button.dart b/widgetbook/lib/ui/widgets/icon_button.dart new file mode 100644 index 0000000..360f063 --- /dev/null +++ b/widgetbook/lib/ui/widgets/icon_button.dart @@ -0,0 +1,17 @@ +import 'package:flutter/widgets.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:groceries_app/ui/ui.dart'; +import 'package:widgetbook_annotation/widgetbook_annotation.dart'; + +@UseCase( + name: 'Default', + type: IconButton, + designLink: + 'https://www.figma.com/design/HsANkdhbsCNTkXBzNJRNLD/Groceries-Demo?node-id=7337-3250&t=N6qwmLP7MP59ClWB-4', +) +Widget buildIconButtonUseCase(BuildContext context) { + return IconButton( + icon: FontAwesomeIcons.user, + onPressed: () {}, + ); +} diff --git a/widgetbook/lib/ui/widgets/navigation_item.dart b/widgetbook/lib/ui/widgets/navigation_item.dart new file mode 100644 index 0000000..196e2bd --- /dev/null +++ b/widgetbook/lib/ui/widgets/navigation_item.dart @@ -0,0 +1,25 @@ +import 'package:flutter/widgets.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:groceries_app/ui/ui.dart'; +import 'package:widgetbook/widgetbook.dart'; +import 'package:widgetbook_annotation/widgetbook_annotation.dart'; + +@UseCase( + name: 'Default', + type: NavigationItem, + designLink: + 'https://www.figma.com/design/HsANkdhbsCNTkXBzNJRNLD/Groceries-Demo?node-id=7395-4299&t=N6qwmLP7MP59ClWB-4', +) +Widget buildNavigationItemUseCase(BuildContext context) { + return NavigationItem( + isSelected: context.knobs.boolean( + label: 'isSelected', + ), + iconSelected: FontAwesomeIcons.solidLemon, + iconUnselected: FontAwesomeIcons.lemon, + text: context.knobs.string( + label: 'text', + initialValue: 'Account', + ), + ); +} diff --git a/widgetbook/lib/ui/widgets/page_shell.dart b/widgetbook/lib/ui/widgets/page_shell.dart new file mode 100644 index 0000000..dcf2d2d --- /dev/null +++ b/widgetbook/lib/ui/widgets/page_shell.dart @@ -0,0 +1,19 @@ +import 'package:flutter/material.dart'; +import 'package:groceries_app/ui/ui.dart'; +import 'package:widgetbook/widgetbook.dart'; +import 'package:widgetbook_annotation/widgetbook_annotation.dart'; + +import '../../features/utility/placeholder.dart' as util; + +@UseCase( + name: 'Default', + type: PageShell, + designLink: + 'https://www.figma.com/design/HsANkdhbsCNTkXBzNJRNLD/Groceries-Demo?node-id=6802-3379&t=N6qwmLP7MP59ClWB-4', +) +Widget buildPageShellUseCase(BuildContext context) { + return PageShell( + header: context.knobs.string(label: 'header', initialValue: 'Header'), + child: const util.Placeholder(), + ); +} diff --git a/widgetbook/lib/ui/widgets/primary_button.dart b/widgetbook/lib/ui/widgets/primary_button.dart new file mode 100644 index 0000000..58d6f18 --- /dev/null +++ b/widgetbook/lib/ui/widgets/primary_button.dart @@ -0,0 +1,23 @@ +// ignore_for_file: unused_import + +import 'package:flutter/widgets.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:groceries_app/ui/ui.dart'; +import 'package:widgetbook/widgetbook.dart'; +import 'package:widgetbook_annotation/widgetbook_annotation.dart'; + +@UseCase( + name: 'Default', + type: PrimaryButton, + designLink: + 'https://www.figma.com/design/HsANkdhbsCNTkXBzNJRNLD/Groceries-Demo?node-id=7235-4663&t=N6qwmLP7MP59ClWB-4', +) +Widget buildPrimaryButtonCase(BuildContext context) { + return PrimaryButton( + content: context.knobs.string(label: 'content', initialValue: 'Text'), + trailing: FaIcon( + FontAwesomeIcons.arrowRight, + color: AppTheme.of(context).text.inverse, + ), + ); +} diff --git a/widgetbook/macos/.gitignore b/widgetbook/macos/.gitignore new file mode 100644 index 0000000..746adbb --- /dev/null +++ b/widgetbook/macos/.gitignore @@ -0,0 +1,7 @@ +# Flutter-related +**/Flutter/ephemeral/ +**/Pods/ + +# Xcode-related +**/dgph +**/xcuserdata/ diff --git a/widgetbook/macos/Flutter/Flutter-Debug.xcconfig b/widgetbook/macos/Flutter/Flutter-Debug.xcconfig new file mode 100644 index 0000000..4b81f9b --- /dev/null +++ b/widgetbook/macos/Flutter/Flutter-Debug.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/widgetbook/macos/Flutter/Flutter-Release.xcconfig b/widgetbook/macos/Flutter/Flutter-Release.xcconfig new file mode 100644 index 0000000..5caa9d1 --- /dev/null +++ b/widgetbook/macos/Flutter/Flutter-Release.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/widgetbook/macos/Flutter/GeneratedPluginRegistrant.swift b/widgetbook/macos/Flutter/GeneratedPluginRegistrant.swift new file mode 100644 index 0000000..e777c67 --- /dev/null +++ b/widgetbook/macos/Flutter/GeneratedPluginRegistrant.swift @@ -0,0 +1,12 @@ +// +// Generated file. Do not edit. +// + +import FlutterMacOS +import Foundation + +import path_provider_foundation + +func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) +} diff --git a/widgetbook/macos/Podfile b/widgetbook/macos/Podfile new file mode 100644 index 0000000..c795730 --- /dev/null +++ b/widgetbook/macos/Podfile @@ -0,0 +1,43 @@ +platform :osx, '10.14' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_macos_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) + target 'RunnerTests' do + inherit! :search_paths + end +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_macos_build_settings(target) + end +end diff --git a/widgetbook/macos/Podfile.lock b/widgetbook/macos/Podfile.lock new file mode 100644 index 0000000..a83eba5 --- /dev/null +++ b/widgetbook/macos/Podfile.lock @@ -0,0 +1,23 @@ +PODS: + - FlutterMacOS (1.0.0) + - path_provider_foundation (0.0.1): + - Flutter + - FlutterMacOS + +DEPENDENCIES: + - FlutterMacOS (from `Flutter/ephemeral`) + - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`) + +EXTERNAL SOURCES: + FlutterMacOS: + :path: Flutter/ephemeral + path_provider_foundation: + :path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin + +SPEC CHECKSUMS: + FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 + path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 + +PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367 + +COCOAPODS: 1.15.2 diff --git a/widgetbook/macos/Runner.xcodeproj/project.pbxproj b/widgetbook/macos/Runner.xcodeproj/project.pbxproj new file mode 100644 index 0000000..a52a5d3 --- /dev/null +++ b/widgetbook/macos/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,801 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXAggregateTarget section */ + 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; + buildPhases = ( + 33CC111E2044C6BF0003C045 /* ShellScript */, + ); + dependencies = ( + ); + name = "Flutter Assemble"; + productName = FLX; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; }; + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; + 9DFA85C992459F384D278DC5 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E6327D031C616A71E9E0B3F7 /* Pods_RunnerTests.framework */; }; + D57EE42FBC9C2BC745F09CAE /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A57FEDD5E41EB45C77798C07 /* Pods_Runner.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC10EC2044A3C60003C045; + remoteInfo = Runner; + }; + 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC111A2044C6BA0003C045; + remoteInfo = FLX; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 33CC110E2044A8840003C045 /* Bundle Framework */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Bundle Framework"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 01BC83508036006089C704C9 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; + 0A728075F5A20DDE27BF64C2 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; + 33CC10ED2044A3C60003C045 /* widgetbook.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = widgetbook.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; + 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; + 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; }; + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; + 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; + 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; + 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; + A57FEDD5E41EB45C77798C07 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + C9A55E0492B8C64ACF94644B /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; + D5710B6E210492A9D46D1C19 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + DB8C6E23023D461E5082DC33 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + E607920E6DFFA6AB49134547 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; + E6327D031C616A71E9E0B3F7 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 331C80D2294CF70F00263BE5 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 9DFA85C992459F384D278DC5 /* Pods_RunnerTests.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10EA2044A3C60003C045 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + D57EE42FBC9C2BC745F09CAE /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 0B74E0DF107AA2E8ECB98EBD /* Pods */ = { + isa = PBXGroup; + children = ( + 0A728075F5A20DDE27BF64C2 /* Pods-Runner.debug.xcconfig */, + D5710B6E210492A9D46D1C19 /* Pods-Runner.release.xcconfig */, + DB8C6E23023D461E5082DC33 /* Pods-Runner.profile.xcconfig */, + C9A55E0492B8C64ACF94644B /* Pods-RunnerTests.debug.xcconfig */, + E607920E6DFFA6AB49134547 /* Pods-RunnerTests.release.xcconfig */, + 01BC83508036006089C704C9 /* Pods-RunnerTests.profile.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; + 331C80D6294CF71000263BE5 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 331C80D7294CF71000263BE5 /* RunnerTests.swift */, + ); + path = RunnerTests; + sourceTree = ""; + }; + 33BA886A226E78AF003329D5 /* Configs */ = { + isa = PBXGroup; + children = ( + 33E5194F232828860026EE4D /* AppInfo.xcconfig */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, + ); + path = Configs; + sourceTree = ""; + }; + 33CC10E42044A3C60003C045 = { + isa = PBXGroup; + children = ( + 33FAB671232836740065AC1E /* Runner */, + 33CEB47122A05771004F2AC0 /* Flutter */, + 331C80D6294CF71000263BE5 /* RunnerTests */, + 33CC10EE2044A3C60003C045 /* Products */, + D73912EC22F37F3D000D13A0 /* Frameworks */, + 0B74E0DF107AA2E8ECB98EBD /* Pods */, + ); + sourceTree = ""; + }; + 33CC10EE2044A3C60003C045 /* Products */ = { + isa = PBXGroup; + children = ( + 33CC10ED2044A3C60003C045 /* widgetbook.app */, + 331C80D5294CF71000263BE5 /* RunnerTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 33CC11242044D66E0003C045 /* Resources */ = { + isa = PBXGroup; + children = ( + 33CC10F22044A3C60003C045 /* Assets.xcassets */, + 33CC10F42044A3C60003C045 /* MainMenu.xib */, + 33CC10F72044A3C60003C045 /* Info.plist */, + ); + name = Resources; + path = ..; + sourceTree = ""; + }; + 33CEB47122A05771004F2AC0 /* Flutter */ = { + isa = PBXGroup; + children = ( + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, + ); + path = Flutter; + sourceTree = ""; + }; + 33FAB671232836740065AC1E /* Runner */ = { + isa = PBXGroup; + children = ( + 33CC10F02044A3C60003C045 /* AppDelegate.swift */, + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, + 33E51913231747F40026EE4D /* DebugProfile.entitlements */, + 33E51914231749380026EE4D /* Release.entitlements */, + 33CC11242044D66E0003C045 /* Resources */, + 33BA886A226E78AF003329D5 /* Configs */, + ); + path = Runner; + sourceTree = ""; + }; + D73912EC22F37F3D000D13A0 /* Frameworks */ = { + isa = PBXGroup; + children = ( + A57FEDD5E41EB45C77798C07 /* Pods_Runner.framework */, + E6327D031C616A71E9E0B3F7 /* Pods_RunnerTests.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 331C80D4294CF70F00263BE5 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + 8988F97713121E9547655361 /* [CP] Check Pods Manifest.lock */, + 331C80D1294CF70F00263BE5 /* Sources */, + 331C80D2294CF70F00263BE5 /* Frameworks */, + 331C80D3294CF70F00263BE5 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 331C80DA294CF71000263BE5 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 331C80D5294CF71000263BE5 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 33CC10EC2044A3C60003C045 /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 9789790E476569D00510E4AA /* [CP] Check Pods Manifest.lock */, + 33CC10E92044A3C60003C045 /* Sources */, + 33CC10EA2044A3C60003C045 /* Frameworks */, + 33CC10EB2044A3C60003C045 /* Resources */, + 33CC110E2044A8840003C045 /* Bundle Framework */, + 3399D490228B24CF009A79C7 /* ShellScript */, + 3F617F77E95343728A3F027A /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 33CC11202044C79F0003C045 /* PBXTargetDependency */, + ); + name = Runner; + productName = Runner; + productReference = 33CC10ED2044A3C60003C045 /* widgetbook.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 33CC10E52044A3C60003C045 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastSwiftUpdateCheck = 0920; + LastUpgradeCheck = 1510; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 331C80D4294CF70F00263BE5 = { + CreatedOnToolsVersion = 14.0; + TestTargetID = 33CC10EC2044A3C60003C045; + }; + 33CC10EC2044A3C60003C045 = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 1100; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.Sandbox = { + enabled = 1; + }; + }; + }; + 33CC111A2044C6BA0003C045 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Manual; + }; + }; + }; + buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 33CC10E42044A3C60003C045; + productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 33CC10EC2044A3C60003C045 /* Runner */, + 331C80D4294CF70F00263BE5 /* RunnerTests */, + 33CC111A2044C6BA0003C045 /* Flutter Assemble */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 331C80D3294CF70F00263BE5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10EB2044A3C60003C045 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3399D490228B24CF009A79C7 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n"; + }; + 33CC111E2044C6BF0003C045 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + Flutter/ephemeral/FlutterInputs.xcfilelist, + ); + inputPaths = ( + Flutter/ephemeral/tripwire, + ); + outputFileListPaths = ( + Flutter/ephemeral/FlutterOutputs.xcfilelist, + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; + }; + 3F617F77E95343728A3F027A /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 8988F97713121E9547655361 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 9789790E476569D00510E4AA /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 331C80D1294CF70F00263BE5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10E92044A3C60003C045 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */, + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 331C80DA294CF71000263BE5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC10EC2044A3C60003C045 /* Runner */; + targetProxy = 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */; + }; + 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; + targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + 33CC10F52044A3C60003C045 /* Base */, + ); + name = MainMenu.xib; + path = Runner; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 331C80DB294CF71000263BE5 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = C9A55E0492B8C64ACF94644B /* Pods-RunnerTests.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.widgetbook.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/widgetbook.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/widgetbook"; + }; + name = Debug; + }; + 331C80DC294CF71000263BE5 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = E607920E6DFFA6AB49134547 /* Pods-RunnerTests.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.widgetbook.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/widgetbook.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/widgetbook"; + }; + name = Release; + }; + 331C80DD294CF71000263BE5 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 01BC83508036006089C704C9 /* Pods-RunnerTests.profile.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.widgetbook.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/widgetbook.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/widgetbook"; + }; + name = Profile; + }; + 338D0CE9231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Profile; + }; + 338D0CEA231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Profile; + }; + 338D0CEB231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Profile; + }; + 33CC10F92044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 33CC10FA2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Release; + }; + 33CC10FC2044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 33CC10FD2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 33CC111C2044C6BA0003C045 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 33CC111D2044C6BA0003C045 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 331C80DB294CF71000263BE5 /* Debug */, + 331C80DC294CF71000263BE5 /* Release */, + 331C80DD294CF71000263BE5 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10F92044A3C60003C045 /* Debug */, + 33CC10FA2044A3C60003C045 /* Release */, + 338D0CE9231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10FC2044A3C60003C045 /* Debug */, + 33CC10FD2044A3C60003C045 /* Release */, + 338D0CEA231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC111C2044C6BA0003C045 /* Debug */, + 33CC111D2044C6BA0003C045 /* Release */, + 338D0CEB231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 33CC10E52044A3C60003C045 /* Project object */; +} diff --git a/widgetbook/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/widgetbook/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/widgetbook/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/widgetbook/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/widgetbook/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000..6e8c1ed --- /dev/null +++ b/widgetbook/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/widgetbook/macos/Runner.xcworkspace/contents.xcworkspacedata b/widgetbook/macos/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..21a3cc1 --- /dev/null +++ b/widgetbook/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/widgetbook/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/widgetbook/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/widgetbook/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/widgetbook/macos/Runner/AppDelegate.swift b/widgetbook/macos/Runner/AppDelegate.swift new file mode 100644 index 0000000..8e02df2 --- /dev/null +++ b/widgetbook/macos/Runner/AppDelegate.swift @@ -0,0 +1,9 @@ +import Cocoa +import FlutterMacOS + +@main +class AppDelegate: FlutterAppDelegate { + override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { + return true + } +} diff --git a/widgetbook/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/widgetbook/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..a2ec33f --- /dev/null +++ b/widgetbook/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_16.png", + "scale" : "1x" + }, + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "2x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "1x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_64.png", + "scale" : "2x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_128.png", + "scale" : "1x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "2x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "1x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "2x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "1x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_1024.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/widgetbook/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/widgetbook/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png new file mode 100644 index 0000000..82b6f9d Binary files /dev/null and b/widgetbook/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png differ diff --git a/widgetbook/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/widgetbook/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png new file mode 100644 index 0000000..13b35eb Binary files /dev/null and b/widgetbook/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png differ diff --git a/widgetbook/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/widgetbook/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png new file mode 100644 index 0000000..0a3f5fa Binary files /dev/null and b/widgetbook/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png differ diff --git a/widgetbook/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/widgetbook/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png new file mode 100644 index 0000000..bdb5722 Binary files /dev/null and b/widgetbook/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png differ diff --git a/widgetbook/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png b/widgetbook/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png new file mode 100644 index 0000000..f083318 Binary files /dev/null and b/widgetbook/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png differ diff --git a/widgetbook/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/widgetbook/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png new file mode 100644 index 0000000..326c0e7 Binary files /dev/null and b/widgetbook/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png differ diff --git a/widgetbook/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/widgetbook/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png new file mode 100644 index 0000000..2f1632c Binary files /dev/null and b/widgetbook/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png differ diff --git a/widgetbook/macos/Runner/Base.lproj/MainMenu.xib b/widgetbook/macos/Runner/Base.lproj/MainMenu.xib new file mode 100644 index 0000000..80e867a --- /dev/null +++ b/widgetbook/macos/Runner/Base.lproj/MainMenu.xib @@ -0,0 +1,343 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/widgetbook/macos/Runner/Configs/AppInfo.xcconfig b/widgetbook/macos/Runner/Configs/AppInfo.xcconfig new file mode 100644 index 0000000..6275254 --- /dev/null +++ b/widgetbook/macos/Runner/Configs/AppInfo.xcconfig @@ -0,0 +1,14 @@ +// Application-level settings for the Runner target. +// +// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the +// future. If not, the values below would default to using the project name when this becomes a +// 'flutter create' template. + +// The application's name. By default this is also the title of the Flutter window. +PRODUCT_NAME = widgetbook + +// The application's bundle identifier +PRODUCT_BUNDLE_IDENTIFIER = com.example.widgetbook + +// The copyright displayed in application information +PRODUCT_COPYRIGHT = Copyright © 2024 com.example. All rights reserved. diff --git a/widgetbook/macos/Runner/Configs/Debug.xcconfig b/widgetbook/macos/Runner/Configs/Debug.xcconfig new file mode 100644 index 0000000..36b0fd9 --- /dev/null +++ b/widgetbook/macos/Runner/Configs/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Debug.xcconfig" +#include "Warnings.xcconfig" diff --git a/widgetbook/macos/Runner/Configs/Release.xcconfig b/widgetbook/macos/Runner/Configs/Release.xcconfig new file mode 100644 index 0000000..dff4f49 --- /dev/null +++ b/widgetbook/macos/Runner/Configs/Release.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Release.xcconfig" +#include "Warnings.xcconfig" diff --git a/widgetbook/macos/Runner/Configs/Warnings.xcconfig b/widgetbook/macos/Runner/Configs/Warnings.xcconfig new file mode 100644 index 0000000..42bcbf4 --- /dev/null +++ b/widgetbook/macos/Runner/Configs/Warnings.xcconfig @@ -0,0 +1,13 @@ +WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings +GCC_WARN_UNDECLARED_SELECTOR = YES +CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES +CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE +CLANG_WARN__DUPLICATE_METHOD_MATCH = YES +CLANG_WARN_PRAGMA_PACK = YES +CLANG_WARN_STRICT_PROTOTYPES = YES +CLANG_WARN_COMMA = YES +GCC_WARN_STRICT_SELECTOR_MATCH = YES +CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES +CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES +GCC_WARN_SHADOW = YES +CLANG_WARN_UNREACHABLE_CODE = YES diff --git a/widgetbook/macos/Runner/DebugProfile.entitlements b/widgetbook/macos/Runner/DebugProfile.entitlements new file mode 100644 index 0000000..c946719 --- /dev/null +++ b/widgetbook/macos/Runner/DebugProfile.entitlements @@ -0,0 +1,14 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.cs.allow-jit + + com.apple.security.network.server + + com.apple.security.network.client + + + diff --git a/widgetbook/macos/Runner/Info.plist b/widgetbook/macos/Runner/Info.plist new file mode 100644 index 0000000..4789daa --- /dev/null +++ b/widgetbook/macos/Runner/Info.plist @@ -0,0 +1,32 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + $(PRODUCT_COPYRIGHT) + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/widgetbook/macos/Runner/MainFlutterWindow.swift b/widgetbook/macos/Runner/MainFlutterWindow.swift new file mode 100644 index 0000000..3cc05eb --- /dev/null +++ b/widgetbook/macos/Runner/MainFlutterWindow.swift @@ -0,0 +1,15 @@ +import Cocoa +import FlutterMacOS + +class MainFlutterWindow: NSWindow { + override func awakeFromNib() { + let flutterViewController = FlutterViewController() + let windowFrame = self.frame + self.contentViewController = flutterViewController + self.setFrame(windowFrame, display: true) + + RegisterGeneratedPlugins(registry: flutterViewController) + + super.awakeFromNib() + } +} diff --git a/widgetbook/macos/Runner/Release.entitlements b/widgetbook/macos/Runner/Release.entitlements new file mode 100644 index 0000000..779a178 --- /dev/null +++ b/widgetbook/macos/Runner/Release.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.network.client + + + diff --git a/widgetbook/macos/RunnerTests/RunnerTests.swift b/widgetbook/macos/RunnerTests/RunnerTests.swift new file mode 100644 index 0000000..61f3bd1 --- /dev/null +++ b/widgetbook/macos/RunnerTests/RunnerTests.swift @@ -0,0 +1,12 @@ +import Cocoa +import FlutterMacOS +import XCTest + +class RunnerTests: XCTestCase { + + func testExample() { + // If you add code to the Runner application, consider adding tests here. + // See https://developer.apple.com/documentation/xctest for more information about using XCTest. + } + +} diff --git a/widgetbook/pubspec.lock b/widgetbook/pubspec.lock index c95a5fd..4c5c9dd 100644 --- a/widgetbook/pubspec.lock +++ b/widgetbook/pubspec.lock @@ -5,15 +5,10 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: f256b0c0ba6c7577c15e2e4e114755640a875e885099367bf6e012b19314c834 + sha256: "0b2f2bd91ba804e53a61d757b986f89f1f9eaed5b11e4b2f5a2468d86d6c9fc7" url: "https://pub.dev" source: hosted - version: "72.0.0" - _macros: - dependency: transitive - description: dart - source: sdk - version: "0.3.2" + version: "67.0.0" accessibility_tools: dependency: transitive description: @@ -26,10 +21,10 @@ packages: dependency: transitive description: name: analyzer - sha256: b652861553cd3990d8ed361f7979dc6d7053a9ac8843fa73820ab68ce5410139 + sha256: "37577842a27e4338429a1cbc32679d508836510b056f1eedf0c8d20e39c1383d" url: "https://pub.dev" source: hosted - version: "6.7.0" + version: "6.4.1" args: dependency: transitive description: @@ -97,18 +92,18 @@ packages: dependency: "direct dev" description: name: build_runner - sha256: dd09dd4e2b078992f42aac7f1a622f01882a8492fef08486b27ddde929c19f04 + sha256: "644dc98a0f179b872f612d3eb627924b578897c629788e858157fa5e704ca0c7" url: "https://pub.dev" source: hosted - version: "2.4.12" + version: "2.4.11" build_runner_core: dependency: transitive description: name: build_runner_core - sha256: f8126682b87a7282a339b871298cc12009cb67109cfa1614d6436fb0289193e0 + sha256: e3c79f69a64bdfcd8a776a3c28db4eb6e3fb5356d013ae5eb2e52007706d5dbe url: "https://pub.dev" source: hosted - version: "7.3.2" + version: "7.3.1" built_collection: dependency: transitive description: @@ -257,14 +252,22 @@ packages: description: flutter source: sdk version: "0.0.0" + font_awesome_flutter: + dependency: "direct main" + description: + name: font_awesome_flutter + sha256: "275ff26905134bcb59417cf60ad979136f1f8257f2f449914b2c3e05bbb4cd6f" + url: "https://pub.dev" + source: hosted + version: "10.7.0" freezed_annotation: dependency: transitive description: name: freezed_annotation - sha256: c2e2d632dd9b8a2b7751117abcfc2b4888ecfe181bd9fca7170d9ef02e595fe2 + sha256: f54946fdb1fa7b01f780841937b1a80783a20b393485f3f6cdf336fd6f4705f2 url: "https://pub.dev" source: hosted - version: "2.4.4" + version: "2.4.2" frontend_server_client: dependency: transitive description: @@ -316,10 +319,10 @@ packages: dependency: transitive description: name: http - sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010 + sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938" url: "https://pub.dev" source: hosted - version: "1.2.2" + version: "1.2.1" http_multi_server: dependency: transitive description: @@ -408,14 +411,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.0" - macros: - dependency: transitive - description: - name: macros - sha256: "0acaed5d6b7eab89f63350bccd82119e6c602df0f391260d0e32b5e23db79536" - url: "https://pub.dev" - source: hosted - version: "0.1.2-main.4" matcher: dependency: transitive description: @@ -476,18 +471,18 @@ packages: dependency: transitive description: name: path_provider - sha256: fec0d61223fba3154d87759e3cc27fe2c8dc498f6386c6d6fc80d1afdd1bf378 + sha256: c9e7d3a4cd1410877472158bee69963a4579f78b68c65a2b7d40d1a7a88bb161 url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.1.3" path_provider_android: dependency: transitive description: name: path_provider_android - sha256: "490539678396d4c3c0b06efdaab75ae60675c3e0c66f72bc04c2e2c1e0e2abeb" + sha256: bca87b0165ffd7cdb9cad8edd22d18d2201e886d9a9f19b4fb3452ea7df3a72a url: "https://pub.dev" source: hosted - version: "2.2.9" + version: "2.2.6" path_provider_foundation: dependency: transitive description: @@ -689,10 +684,10 @@ packages: dependency: transitive description: name: vm_service - sha256: f652077d0bdf60abe4c1f6377448e8655008eef28f128bc023f7b5e8dfeb48fc + sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" url: "https://pub.dev" source: hosted - version: "14.2.4" + version: "14.2.5" watcher: dependency: transitive description: @@ -705,26 +700,26 @@ packages: dependency: transitive description: name: web - sha256: d43c1d6b787bf0afad444700ae7f4db8827f701bc61c255ac8d328c6f4d52062 + sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "0.5.1" web_socket: dependency: transitive description: name: web_socket - sha256: "3c12d96c0c9a4eec095246debcea7b86c0324f22df69893d538fcc6f1b8cce83" + sha256: "24301d8c293ce6fe327ffe6f59d8fd8834735f0ec36e4fd383ec7ff8a64aa078" url: "https://pub.dev" source: hosted - version: "0.1.6" + version: "0.1.5" web_socket_channel: dependency: transitive description: name: web_socket_channel - sha256: "9f187088ed104edd8662ca07af4b124465893caf063ba29758f97af57e61da8f" + sha256: a2d56211ee4d35d9b344d9d4ce60f362e4f5d1aafb988302906bd732bc731276 url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.0.0" widgetbook: dependency: "direct main" description: @@ -766,5 +761,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.5.0-259.0.dev <4.0.0" + dart: ">=3.4.0 <4.0.0" flutter: ">=3.24.0" diff --git a/widgetbook/pubspec.yaml b/widgetbook/pubspec.yaml index 36de2b6..861c695 100644 --- a/widgetbook/pubspec.yaml +++ b/widgetbook/pubspec.yaml @@ -11,6 +11,7 @@ dependencies: path: ../assets flutter: sdk: flutter + font_awesome_flutter: ^10.7.0 groceries_app: path: ../ widgetbook: ^3.8.0