From 1a53d7b583888999f5abfd592fadabe2339d34bd Mon Sep 17 00:00:00 2001 From: Jonas Franz Date: Thu, 24 Feb 2022 01:42:40 +0100 Subject: [PATCH] Add user and product providers --- lib/app.dart | 29 ++++++++++++------- lib/models/product.dart | 2 ++ lib/screens/cart/cart_screen.dart | 2 +- .../product_list/product_list_screen.dart | 27 ++++++++--------- lib/stores/product_list_provider.dart | 13 +++++++++ lib/stores/product_loading_provider.dart | 6 ++++ lib/stores/product_service_provider.dart | 16 ++++++++++ lib/stores/user_store.dart | 12 ++++++++ lib/widgets/user_switch.dart | 18 +++++------- pubspec.lock | 22 ++++++++++++++ pubspec.yaml | 3 +- 11 files changed, 112 insertions(+), 38 deletions(-) create mode 100644 lib/stores/product_list_provider.dart create mode 100644 lib/stores/product_loading_provider.dart create mode 100644 lib/stores/product_service_provider.dart create mode 100644 lib/stores/user_store.dart diff --git a/lib/app.dart b/lib/app.dart index c755039..75b7374 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -1,8 +1,10 @@ import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:thesis_shop/route_key.dart'; import 'package:thesis_shop/screens/cart/cart_screen.dart'; import 'package:thesis_shop/screens/product_list/product_list_screen.dart'; import 'package:thesis_shop/service/product_service.dart'; +import 'package:thesis_shop/stores/product_service_provider.dart'; import 'package:thesis_shop/utils/map_keys_extension.dart'; class ThesisShopApp extends StatelessWidget { @@ -12,18 +14,23 @@ class ThesisShopApp extends StatelessWidget { @override Widget build(BuildContext context) { - return MaterialApp( - title: 'Thesis Shop', - theme: ThemeData(primarySwatch: Colors.red), - darkTheme: ThemeData( - primarySwatch: Colors.red, - brightness: Brightness.dark, + return ProviderScope( + overrides: [ + productServiceProvider.overrideWithValue(productService), + ], + child: MaterialApp( + title: 'Thesis Shop', + theme: ThemeData(primarySwatch: Colors.red), + darkTheme: ThemeData( + primarySwatch: Colors.red, + brightness: Brightness.dark, + ), + routes: { + RouteKey.products: (context) => const ProductListScreen(), + RouteKey.cart: (context) => const CartScreen(), + }.mapKeys((key) => key.name), + initialRoute: 'products', ), - routes: { - RouteKey.products: (context) => const ProductListScreen(), - RouteKey.cart: (context) => const CartScreen(), - }.mapKeys((key) => key.name), - initialRoute: 'products', ); } } diff --git a/lib/models/product.dart b/lib/models/product.dart index 1b73556..e35f35f 100644 --- a/lib/models/product.dart +++ b/lib/models/product.dart @@ -8,4 +8,6 @@ class Product { String get priceAsString => price.toStringAsFixed(2); const Product({required this.title, required this.price}); + + Product copyWithDiscount() => Product(title: title, price: price * 0.8); } diff --git a/lib/screens/cart/cart_screen.dart b/lib/screens/cart/cart_screen.dart index 08e9e45..e369485 100644 --- a/lib/screens/cart/cart_screen.dart +++ b/lib/screens/cart/cart_screen.dart @@ -20,7 +20,7 @@ class CartScreen extends StatelessWidget { return Scaffold( appBar: AppBar( title: const Text('Warenkorb'), - actions: [UserSwitch(isOn: true, onChanged: (_) {})], + actions: const [UserSwitch()], ), body: Column( mainAxisSize: MainAxisSize.max, diff --git a/lib/screens/product_list/product_list_screen.dart b/lib/screens/product_list/product_list_screen.dart index 30ce3a8..a03dbbd 100644 --- a/lib/screens/product_list/product_list_screen.dart +++ b/lib/screens/product_list/product_list_screen.dart @@ -1,31 +1,28 @@ import 'package:flutter/material.dart'; -import 'package:thesis_shop/models/product.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:thesis_shop/stores/product_list_provider.dart'; import 'package:thesis_shop/widgets/user_switch.dart'; import 'cart_button_overlay.dart'; import 'product_list.dart'; -const _productPlaceholder = [ - Product(title: 'Bananen', price: 3), - Product(title: 'Äpfel', price: 2), - Product(title: 'Birnen', price: 2.5), - Product(title: 'Kirschen', price: 1.2), -]; - -class ProductListScreen extends StatelessWidget { +class ProductListScreen extends ConsumerWidget { const ProductListScreen({Key? key}) : super(key: key); @override - Widget build(BuildContext context) { - const products = _productPlaceholder; + Widget build(BuildContext context, ref) { return Scaffold( appBar: AppBar( title: const Text('Thesis Shop'), - actions: [UserSwitch(isOn: true, onChanged: (_) {})], - ), - body: const CartButtonOverlay( - child: ProductList(products: products), + actions: const [UserSwitch()], ), + body: ref.watch(productListProvider).when( + data: (products) => CartButtonOverlay( + child: ProductList(products: products), + ), + error: (error, _) => Center(child: Text(error.toString())), + loading: () => const Center(child: CircularProgressIndicator()), + ), ); } } diff --git a/lib/stores/product_list_provider.dart b/lib/stores/product_list_provider.dart new file mode 100644 index 0000000..efbaafa --- /dev/null +++ b/lib/stores/product_list_provider.dart @@ -0,0 +1,13 @@ +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:thesis_shop/models/product.dart'; +import 'package:thesis_shop/stores/product_loading_provider.dart'; +import 'package:thesis_shop/stores/user_store.dart'; + +final productListProvider = Provider>>((ref) { + final isSignedIn = ref.watch(userStoreProvider); + if (!isSignedIn) { + return ref.watch(productLoadingProvider); + } + return ref.watch(productLoadingProvider).whenData( + (products) => products.map((pro) => pro.copyWithDiscount()).toList()); +}); diff --git a/lib/stores/product_loading_provider.dart b/lib/stores/product_loading_provider.dart new file mode 100644 index 0000000..3e803ba --- /dev/null +++ b/lib/stores/product_loading_provider.dart @@ -0,0 +1,6 @@ +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:thesis_shop/stores/product_service_provider.dart'; + +final productLoadingProvider = FutureProvider( + (ref) => ref.read(productServiceProvider).fetchProducts(), + dependencies: [productServiceProvider]); diff --git a/lib/stores/product_service_provider.dart b/lib/stores/product_service_provider.dart new file mode 100644 index 0000000..a76dab7 --- /dev/null +++ b/lib/stores/product_service_provider.dart @@ -0,0 +1,16 @@ +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:thesis_shop/models/product.dart'; +import 'package:thesis_shop/service/product_service.dart'; + +class ProductServiceImplementationPlaceholder implements ProductService { + @override + Future> fetchProducts() { + throw UnimplementedError(); + } + + @override + String get url => throw UnimplementedError(); +} + +final productServiceProvider = Provider( + (ref) => ProductServiceImplementationPlaceholder()); diff --git a/lib/stores/user_store.dart b/lib/stores/user_store.dart new file mode 100644 index 0000000..9ee433b --- /dev/null +++ b/lib/stores/user_store.dart @@ -0,0 +1,12 @@ +import 'package:riverpod/riverpod.dart'; + +class UserStore extends StateNotifier { + UserStore({bool initialSignInStatus = false}) : super(initialSignInStatus); + + void changeSignInStatus(bool newStatus) { + state = newStatus; + } +} + +final userStoreProvider = + StateNotifierProvider((ref) => UserStore()); diff --git a/lib/widgets/user_switch.dart b/lib/widgets/user_switch.dart index 63107e7..a579d68 100644 --- a/lib/widgets/user_switch.dart +++ b/lib/widgets/user_switch.dart @@ -1,24 +1,22 @@ import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:thesis_shop/benchmark_counter.dart'; +import 'package:thesis_shop/stores/user_store.dart'; -class UserSwitch extends StatelessWidget { - final bool isOn; - final ValueChanged onChanged; - const UserSwitch({ - required this.isOn, - required this.onChanged, - }) : super(key: const Key('user_switch')); +class UserSwitch extends ConsumerWidget { + const UserSwitch() : super(key: const Key('user_switch')); @override - Widget build(BuildContext context) { + Widget build(BuildContext context, ref) { + final isSignedIn = ref.watch(userStoreProvider); BenchmarkCounters.userSwitch++; return Row( mainAxisSize: MainAxisSize.min, children: [ const Icon(Icons.account_circle), Switch( - value: isOn, - onChanged: onChanged, + value: isSignedIn, + onChanged: ref.read(userStoreProvider.notifier).changeSignInStatus, activeColor: Colors.green, ) ], diff --git a/pubspec.lock b/pubspec.lock index e8671af..d7d55a5 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -146,6 +146,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.4" + flutter_riverpod: + dependency: "direct main" + description: + name: flutter_riverpod + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.3" flutter_test: dependency: "direct dev" description: flutter @@ -228,6 +235,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.0" + riverpod: + dependency: "direct main" + description: + name: riverpod + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.3" sky_engine: dependency: transitive description: flutter @@ -247,6 +261,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.10.0" + state_notifier: + dependency: transitive + description: + name: state_notifier + url: "https://pub.dartlang.org" + source: hosted + version: "0.7.2+1" stream_channel: dependency: transitive description: @@ -305,3 +326,4 @@ packages: version: "3.1.0" sdks: dart: ">=2.16.1 <3.0.0" + flutter: ">=1.17.0" diff --git a/pubspec.yaml b/pubspec.yaml index 3642c34..8345097 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -30,6 +30,8 @@ dependencies: flutter: sdk: flutter http: ^0.13.4 + riverpod: 1.0.3 + flutter_riverpod: 1.0.3 dev_dependencies: flutter_test: sdk: flutter @@ -40,7 +42,6 @@ dev_dependencies: # rules and activating additional ones. flutter_lints: ^1.0.0 dart_code_metrics: ^4.10.1 - # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec