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: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 '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 { |
||||
const ProductListScreen({Key? key}) : super(key: key); |
||||
|
||||
@override |
||||
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( |
||||
appBar: AppBar( |
||||
title: const Text('Thesis Shop'), |
||||
actions: [UserSwitch(isOn: true, onChanged: (_) {})], |
||||
), |
||||
body: const CartButtonOverlay( |
||||
child: ProductList(products: products), |
||||
), |
||||
body: StreamBuilder<ProductsState>( |
||||
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