parent
dac07065ff
commit
7736d692f3
@ -0,0 +1,53 @@ |
|||||||
|
import 'dart:async'; |
||||||
|
|
||||||
|
import 'package:thesis_shop/models/product.dart'; |
||||||
|
import 'package:thesis_shop/service/product_service.dart'; |
||||||
|
import 'package:thesis_shop/utils/disposable.dart'; |
||||||
|
|
||||||
|
part 'product_bloc_events.dart'; |
||||||
|
part 'product_bloc_states.dart'; |
||||||
|
|
||||||
|
class ProductBloc implements Disposable { |
||||||
|
final ProductService _productService; |
||||||
|
|
||||||
|
var _productsState = ProductsState.loading(); |
||||||
|
ProductsState get productsState => _productsState; |
||||||
|
|
||||||
|
final _productsStateController = StreamController<ProductsState>.broadcast(); |
||||||
|
Sink<ProductsState> get _productsStateSink => _productsStateController.sink; |
||||||
|
Stream<ProductsState> get productsStateStream => |
||||||
|
_productsStateController.stream; |
||||||
|
|
||||||
|
final _loadProductEventController = |
||||||
|
StreamController<LoadProductEvent>.broadcast(); |
||||||
|
Sink<LoadProductEvent> get loadProductSink => |
||||||
|
_loadProductEventController.sink; |
||||||
|
|
||||||
|
ProductBloc(this._productService) { |
||||||
|
_handleLoadProductEvents(); |
||||||
|
} |
||||||
|
|
||||||
|
void _handleLoadProductEvents() { |
||||||
|
_loadProductEventController.stream.listen((_) async { |
||||||
|
_productsState = ProductsState.loading(); |
||||||
|
_productsStateSink.add(_productsState); |
||||||
|
|
||||||
|
try { |
||||||
|
final products = await _productService.fetchProducts(); |
||||||
|
_productsState = ProductsState.loaded(products); |
||||||
|
_productsStateSink.add(_productsState); |
||||||
|
} catch (err) { |
||||||
|
_productsState = ProductsState.error(err.toString()); |
||||||
|
_productsStateSink.add(_productsState); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
@override |
||||||
|
Future<void> dispose() { |
||||||
|
return Future.wait([ |
||||||
|
_productsStateController.close(), |
||||||
|
_loadProductEventController.close(), |
||||||
|
]); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,5 @@ |
|||||||
|
part of 'product_bloc.dart'; |
||||||
|
|
||||||
|
class LoadProductEvent { |
||||||
|
const LoadProductEvent(); |
||||||
|
} |
@ -0,0 +1,31 @@ |
|||||||
|
part of 'product_bloc.dart'; |
||||||
|
|
||||||
|
abstract class ProductsState { |
||||||
|
const ProductsState._(); |
||||||
|
|
||||||
|
factory ProductsState.loading() => const ProductsLoading(); |
||||||
|
factory ProductsState.error(String errorMessage) => |
||||||
|
ProductsError(errorMessage); |
||||||
|
factory ProductsState.loaded(List<Product> products) => |
||||||
|
ProductsLoaded(products); |
||||||
|
|
||||||
|
bool isLoading() => this is ProductsLoading; |
||||||
|
bool isLoaded() => this is ProductsLoaded; |
||||||
|
bool hasError() => this is ProductsError; |
||||||
|
|
||||||
|
T as<T extends ProductsState>() => this as T; |
||||||
|
} |
||||||
|
|
||||||
|
class ProductsLoading extends ProductsState { |
||||||
|
const ProductsLoading() : super._(); |
||||||
|
} |
||||||
|
|
||||||
|
class ProductsError extends ProductsState { |
||||||
|
final String errorMessage; |
||||||
|
const ProductsError(this.errorMessage) : super._(); |
||||||
|
} |
||||||
|
|
||||||
|
class ProductsLoaded extends ProductsState { |
||||||
|
final List<Product> products; |
||||||
|
const ProductsLoaded(this.products) : super._(); |
||||||
|
} |
@ -1,31 +1,46 @@ |
|||||||
import 'package:flutter/material.dart'; |
import 'package:flutter/material.dart'; |
||||||
import 'package:thesis_shop/models/product.dart'; |
import 'package:thesis_shop/bloc/product_bloc.dart'; |
||||||
|
import 'package:thesis_shop/bloc_provider.dart'; |
||||||
import 'package:thesis_shop/widgets/user_switch.dart'; |
import 'package:thesis_shop/widgets/user_switch.dart'; |
||||||
|
|
||||||
import 'cart_button_overlay.dart'; |
import 'cart_button_overlay.dart'; |
||||||
import 'product_list.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 StatelessWidget { |
||||||
const ProductListScreen({Key? key}) : super(key: key); |
const ProductListScreen({Key? key}) : super(key: key); |
||||||
|
|
||||||
@override |
@override |
||||||
Widget build(BuildContext context) { |
Widget build(BuildContext context) { |
||||||
const products = _productPlaceholder; |
final productBloc = AppState.of(context).blocProvider.productBloc; |
||||||
|
if (productBloc.productsState.isLoading()) { |
||||||
|
productBloc.loadProductSink.add(const LoadProductEvent()); |
||||||
|
} |
||||||
return Scaffold( |
return Scaffold( |
||||||
appBar: AppBar( |
appBar: AppBar( |
||||||
title: const Text('Thesis Shop'), |
title: const Text('Thesis Shop'), |
||||||
actions: [UserSwitch(isOn: true, onChanged: (_) {})], |
actions: [UserSwitch(isOn: true, onChanged: (_) {})], |
||||||
), |
), |
||||||
body: const CartButtonOverlay( |
body: StreamBuilder<ProductsState>( |
||||||
child: ProductList(products: products), |
stream: productBloc.productsStateStream, |
||||||
), |
initialData: productBloc.productsState, |
||||||
|
builder: (context, snapshot) { |
||||||
|
final state = snapshot.requireData; |
||||||
|
if (state.isLoaded()) { |
||||||
|
return CartButtonOverlay( |
||||||
|
child: |
||||||
|
ProductList(products: state.as<ProductsLoaded>().products), |
||||||
|
); |
||||||
|
} else if (state.hasError()) { |
||||||
|
return Center( |
||||||
|
child: Text(state.as<ProductsError>().errorMessage), |
||||||
|
); |
||||||
|
} else if (state.isLoading()) { |
||||||
|
return const Center( |
||||||
|
child: CircularProgressIndicator.adaptive(), |
||||||
|
); |
||||||
|
} |
||||||
|
throw UnimplementedError(); |
||||||
|
}), |
||||||
); |
); |
||||||
} |
} |
||||||
} |
} |
||||||
|
Loading…
Reference in new issue