1
0
Fork 0

Add product bloc

bloc
Jonas Franz 2 years ago
parent dac07065ff
commit 7736d692f3
  1. 2
      lib/app.dart
  2. 53
      lib/bloc/product_bloc.dart
  3. 5
      lib/bloc/product_bloc_events.dart
  4. 31
      lib/bloc/product_bloc_states.dart
  5. 8
      lib/bloc_provider.dart
  6. 39
      lib/screens/product_list/product_list_screen.dart

@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:thesis_shop/bloc/product_bloc.dart';
import 'package:thesis_shop/bloc/user_bloc.dart';
import 'package:thesis_shop/bloc_provider.dart';
import 'package:thesis_shop/route_key.dart';
@ -14,6 +15,7 @@ class ThesisShopApp extends StatelessWidget {
BlocProvider setupBlocs() {
return BlocProvider(
productBloc: ProductBloc(productService),
userBloc: UserBloc(),
);
}

@ -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,17 +1,23 @@
import 'package:flutter/widgets.dart';
import 'package:thesis_shop/bloc/product_bloc.dart';
import 'package:thesis_shop/bloc/user_bloc.dart';
import 'utils/disposable.dart';
class BlocProvider implements Disposable {
final UserBloc userBloc;
final ProductBloc productBloc;
const BlocProvider({required this.userBloc});
const BlocProvider({
required this.userBloc,
required this.productBloc,
});
@override
Future<void> dispose() async {
await Future.wait<void>([
userBloc.dispose(),
productBloc.dispose(),
]);
}
}

@ -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…
Cancel
Save