From b171fef348eb79a406f7181cb53c6cc69f7adad1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20M=C3=BCller?= Date: Tue, 7 May 2024 22:15:56 +0200 Subject: [PATCH] repository and data provider concept --- .../dataLoader/data_loader.dart | 3 +- .../loadableState/loadable_hydrated_bloc.dart | 25 ------- .../loadable_state_background_loading.dart | 6 +- .../view/loadable_state_consumer.dart | 18 ++--- .../view/loadable_state_error_bar.dart | 4 +- .../view/loadable_state_primary_loading.dart | 6 +- .../infrastructure/repository/repository.dart | 14 +--- ...roviding_builder.dart => bloc_module.dart} | 4 +- .../loadable_hydrated_bloc.dart | 31 +++++++++ .../loadable_hydrated_bloc_event.dart | 7 ++ .../bloc/marianum_message_bloc.dart | 29 ++++++-- .../marianum_message_get_messages.dart | 9 ++- .../marianum_message_repository.dart | 8 +-- .../view/marianum_message_list_view.dart | 69 ++++++++++--------- 14 files changed, 129 insertions(+), 104 deletions(-) delete mode 100644 lib/state/app/infrastructure/loadableState/loadable_hydrated_bloc.dart rename lib/state/app/infrastructure/utilityWidgets/{bloc_providing_builder.dart => bloc_module.dart} (64%) create mode 100644 lib/state/app/infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc.dart create mode 100644 lib/state/app/infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc_event.dart diff --git a/lib/state/app/infrastructure/dataLoader/data_loader.dart b/lib/state/app/infrastructure/dataLoader/data_loader.dart index 6f84cb1..47306ba 100644 --- a/lib/state/app/infrastructure/dataLoader/data_loader.dart +++ b/lib/state/app/infrastructure/dataLoader/data_loader.dart @@ -1,4 +1,3 @@ abstract class DataLoader { - - Future fetch(); + Future fetch(); } diff --git a/lib/state/app/infrastructure/loadableState/loadable_hydrated_bloc.dart b/lib/state/app/infrastructure/loadableState/loadable_hydrated_bloc.dart deleted file mode 100644 index 5694249..0000000 --- a/lib/state/app/infrastructure/loadableState/loadable_hydrated_bloc.dart +++ /dev/null @@ -1,25 +0,0 @@ -import 'package:hydrated_bloc/hydrated_bloc.dart'; - -import '../repository/repository.dart'; -import 'loadable_state.dart'; - -sealed class LoadableHydratedBlocEvent {} -class DataRecieved extends LoadableHydratedBlocEvent {} - -abstract class LoadableHydratedBloc extends HydratedBloc> { - LoadableHydratedBloc() : super(const LoadableState()) { - repository().load(); - - } - - Repository repository(); - - - @override - fromJson(Map json) => LoadableState(isLoading: true, data: fromStorage(json)); - @override - Map? toJson(state) => state.data.toJson(); - - TState fromStorage(Map json); - Map? toStorage(TState state); -} diff --git a/lib/state/app/infrastructure/loadableState/view/loadable_state_background_loading.dart b/lib/state/app/infrastructure/loadableState/view/loadable_state_background_loading.dart index 1c10bb0..5147e45 100644 --- a/lib/state/app/infrastructure/loadableState/view/loadable_state_background_loading.dart +++ b/lib/state/app/infrastructure/loadableState/view/loadable_state_background_loading.dart @@ -1,14 +1,14 @@ import 'package:flutter/material.dart'; +import 'loadable_state_consumer.dart'; + class LoadableStateBackgroundLoading extends StatelessWidget { final bool visible; const LoadableStateBackgroundLoading({required this.visible, super.key}); - final Duration animationDuration = const Duration(milliseconds: 200); - @override Widget build(BuildContext context) => AnimatedSwitcher( - duration: animationDuration, + duration: LoadableStateConsumer.animationDuration, transitionBuilder: (Widget child, Animation animation) => SlideTransition( position: Tween( begin: const Offset(0.0, -1.0), diff --git a/lib/state/app/infrastructure/loadableState/view/loadable_state_consumer.dart b/lib/state/app/infrastructure/loadableState/view/loadable_state_consumer.dart index c3d3c75..513a13e 100644 --- a/lib/state/app/infrastructure/loadableState/view/loadable_state_consumer.dart +++ b/lib/state/app/infrastructure/loadableState/view/loadable_state_consumer.dart @@ -2,36 +2,36 @@ import 'package:bloc/bloc.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import '../../utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc_event.dart'; import '../loadable_state.dart'; import 'loadable_state_background_loading.dart'; import 'loadable_state_error_bar.dart'; import 'loadable_state_primary_loading.dart'; -// TODO might be a simpler way -class LoadableStateConsumer, TWrappedState extends LoadableState, TState> extends StatelessWidget { - final Widget Function(TState state) child; +class LoadableStateConsumer, LoadableState>, TState> extends StatelessWidget { + final Widget Function(TState state, bool loading) child; const LoadableStateConsumer({required this.child, super.key}); static Duration animationDuration = const Duration(milliseconds: 200); @override Widget build(BuildContext context) { - var state = context.read().state as LoadableState; + var loadableState = context.watch().state; return Column( children: [ - LoadableStateErrorBar(visible: state.showError()), + LoadableStateErrorBar(visible: loadableState.showError()), Expanded( child: Stack( children: [ - LoadableStatePrimaryLoading(visible: state.showPrimaryLoading()), - LoadableStateBackgroundLoading(visible: state.showBackgroundLoading()), + LoadableStatePrimaryLoading(visible: loadableState.showPrimaryLoading()), + LoadableStateBackgroundLoading(visible: loadableState.showBackgroundLoading()), AnimatedOpacity( - opacity: state.showContent() ? 1.0 : 0.0, + opacity: loadableState.showContent() ? 1.0 : 0.0, duration: animationDuration, curve: Curves.easeInOut, - child: state.showContent() ? child(state.data) : const SizedBox.shrink() + child: loadableState.showContent() ? child(loadableState.data, loadableState.isLoading) : const SizedBox.shrink() ), ], ), diff --git a/lib/state/app/infrastructure/loadableState/view/loadable_state_error_bar.dart b/lib/state/app/infrastructure/loadableState/view/loadable_state_error_bar.dart index 4e21c72..b5c1f55 100644 --- a/lib/state/app/infrastructure/loadableState/view/loadable_state_error_bar.dart +++ b/lib/state/app/infrastructure/loadableState/view/loadable_state_error_bar.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import '../../utilityWidgets/bloc_providing_builder.dart'; +import '../../utilityWidgets/bloc_module.dart'; import '../bloc/loadable_state_bloc.dart'; import '../bloc/loadable_state_state.dart'; @@ -12,7 +12,7 @@ class LoadableStateErrorBar extends StatelessWidget { final Duration animationDuration = const Duration(milliseconds: 200); @override - Widget build(BuildContext context) => BlocProvidingBuilder( + Widget build(BuildContext context) => BlocModule( create: (context) => LoadableStateBloc(), child: (context, state) => AnimatedSize( duration: animationDuration, diff --git a/lib/state/app/infrastructure/loadableState/view/loadable_state_primary_loading.dart b/lib/state/app/infrastructure/loadableState/view/loadable_state_primary_loading.dart index a01ba28..053aaba 100644 --- a/lib/state/app/infrastructure/loadableState/view/loadable_state_primary_loading.dart +++ b/lib/state/app/infrastructure/loadableState/view/loadable_state_primary_loading.dart @@ -1,15 +1,15 @@ import 'package:flutter/material.dart'; +import 'loadable_state_consumer.dart'; + class LoadableStatePrimaryLoading extends StatelessWidget { final bool visible; const LoadableStatePrimaryLoading({required this.visible, super.key}); - final Duration animationDuration = const Duration(milliseconds: 200); - @override Widget build(BuildContext context) => AnimatedOpacity( opacity: visible ? 1.0 : 0.0, - duration: animationDuration, + duration: LoadableStateConsumer.animationDuration, curve: Curves.easeInOut, child: const Center(child: CircularProgressIndicator()), ); diff --git a/lib/state/app/infrastructure/repository/repository.dart b/lib/state/app/infrastructure/repository/repository.dart index 4a6caae..58a4e85 100644 --- a/lib/state/app/infrastructure/repository/repository.dart +++ b/lib/state/app/infrastructure/repository/repository.dart @@ -1,13 +1 @@ -import '../dataLoader/data_loader.dart'; - -abstract class Repository { - final List dataLoaders; - - Repository(this.dataLoaders); - - Future load() async { - dataLoaders.forEach((element) { - element.fetch(); - }); - } -} +abstract class Repository {} diff --git a/lib/state/app/infrastructure/utilityWidgets/bloc_providing_builder.dart b/lib/state/app/infrastructure/utilityWidgets/bloc_module.dart similarity index 64% rename from lib/state/app/infrastructure/utilityWidgets/bloc_providing_builder.dart rename to lib/state/app/infrastructure/utilityWidgets/bloc_module.dart index 4c98579..3a61b9c 100644 --- a/lib/state/app/infrastructure/utilityWidgets/bloc_providing_builder.dart +++ b/lib/state/app/infrastructure/utilityWidgets/bloc_module.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -class BlocProvidingBuilder, TState> extends StatelessWidget { +class BlocModule, TState> extends StatelessWidget { final TBloc Function(BuildContext context) create; final Widget Function(BuildContext context, TState state) child; - const BlocProvidingBuilder({required this.create, required this.child, super.key}); + const BlocModule({required this.create, required this.child, super.key}); @override Widget build(BuildContext context) => BlocProvider(create: create, child: BlocBuilder(builder: child)); diff --git a/lib/state/app/infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc.dart b/lib/state/app/infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc.dart new file mode 100644 index 0000000..7f6d8a8 --- /dev/null +++ b/lib/state/app/infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc.dart @@ -0,0 +1,31 @@ +import 'package:hydrated_bloc/hydrated_bloc.dart'; + +import '../../repository/repository.dart'; +import 'loadable_hydrated_bloc_event.dart'; +import '../../loadableState/loadable_state.dart'; + +abstract class LoadableHydratedBloc, TState, TRepository extends Repository> extends HydratedBloc, LoadableState> { + late TRepository _repository; + LoadableHydratedBloc() : super(const LoadableState()) { + on>((event, emit) => emit(LoadableState(isLoading: event.loading, data: event.state(innerState ?? fromNothing())))); + on>((event, emit) => emit(const LoadableState())); + + _repository = repository(); + loadState(); + } + + TState? get innerState => state.data; + TRepository get repo => _repository; + + @override + fromJson(Map json) => LoadableState(isLoading: true, data: fromStorage(json)); + @override + Map? toJson(LoadableState state) => state.data == null ? {} : state.data.toJson(); + + Future loadState(); + TRepository repository(); + + TState fromNothing(); + TState fromStorage(Map json); + Map? toStorage(TState state); +} diff --git a/lib/state/app/infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc_event.dart b/lib/state/app/infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc_event.dart new file mode 100644 index 0000000..a4b3580 --- /dev/null +++ b/lib/state/app/infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc_event.dart @@ -0,0 +1,7 @@ +class LoadableHydratedBlocEvent {} +class Emit extends LoadableHydratedBlocEvent { + final TState Function(TState state) state; + final bool loading; + Emit(this.state, {this.loading = false}); +} +class ClearState extends LoadableHydratedBlocEvent {} \ No newline at end of file diff --git a/lib/state/app/modules/marianumMessage/bloc/marianum_message_bloc.dart b/lib/state/app/modules/marianumMessage/bloc/marianum_message_bloc.dart index 0563ae3..b960e9c 100644 --- a/lib/state/app/modules/marianumMessage/bloc/marianum_message_bloc.dart +++ b/lib/state/app/modules/marianumMessage/bloc/marianum_message_bloc.dart @@ -1,17 +1,32 @@ -import '../../../infrastructure/loadableState/loadable_hydrated_bloc.dart'; -import '../../../infrastructure/repository/repository.dart'; +import '../../../infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc.dart'; +import '../../../infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc_event.dart'; import '../repository/marianum_message_repository.dart'; import 'marianum_message_state.dart'; -class MarianumMessageBloc extends LoadableHydratedBloc { - MarianumMessageBloc(); +sealed class MarianumMessageEvent extends LoadableHydratedBlocEvent {} +class MessageEvent extends MarianumMessageEvent {} +class MarianumMessageBloc extends LoadableHydratedBloc { + MarianumMessageBloc() { + on((event, emit) { + add(Emit((state) => state.copyWith.messageList(messages: []))); + }); + } + + @override + Future loadState() async { + var messages = await repo.getMessages(); + add(Emit((state) => state.copyWith(messageList: messages))); + } + + @override + MarianumMessageRepository repository() => MarianumMessageRepository(); + + @override + MarianumMessageState fromNothing() => const MarianumMessageState(messageList: MarianumMessageList(base: "", messages: [])); @override MarianumMessageState fromStorage(Map json) => MarianumMessageState.fromJson(json); @override Map? toStorage(MarianumMessageState state) => state.toJson(); - - @override - Repository repository() => MarianumMessageRepository(emit); } diff --git a/lib/state/app/modules/marianumMessage/dataProvider/marianum_message_get_messages.dart b/lib/state/app/modules/marianumMessage/dataProvider/marianum_message_get_messages.dart index 694b304..4e0f188 100644 --- a/lib/state/app/modules/marianumMessage/dataProvider/marianum_message_get_messages.dart +++ b/lib/state/app/modules/marianumMessage/dataProvider/marianum_message_get_messages.dart @@ -1,6 +1,11 @@ import '../../../infrastructure/dataLoader/data_loader.dart'; +import '../bloc/marianum_message_state.dart'; + +class MarianumMessageGetMessages extends DataLoader { -class MarianumMessageGetMessages extends DataLoader { @override - Future fetch() => Future(() => 'Test'); + Future fetch() async { + await Future.delayed(const Duration(seconds: 3)); + return const MarianumMessageList(base: '', messages: [MarianumMessage(date: '', name: 'RepoTest', url: '')]); + } } diff --git a/lib/state/app/modules/marianumMessage/repository/marianum_message_repository.dart b/lib/state/app/modules/marianumMessage/repository/marianum_message_repository.dart index 9679f44..033f821 100644 --- a/lib/state/app/modules/marianumMessage/repository/marianum_message_repository.dart +++ b/lib/state/app/modules/marianumMessage/repository/marianum_message_repository.dart @@ -1,9 +1,7 @@ -import '../../../infrastructure/loadableState/loadable_state.dart'; import '../../../infrastructure/repository/repository.dart'; +import '../bloc/marianum_message_state.dart'; import '../dataProvider/marianum_message_get_messages.dart'; -class MarianumMessageRepository extends Repository { - MarianumMessageRepository(void Function(LoadableState content) emit) : super([ - MarianumMessageGetMessages(), - ]); +class MarianumMessageRepository extends Repository { + Future getMessages() => MarianumMessageGetMessages().fetch(); } diff --git a/lib/state/app/modules/marianumMessage/view/marianum_message_list_view.dart b/lib/state/app/modules/marianumMessage/view/marianum_message_list_view.dart index fa323c7..1fddef3 100644 --- a/lib/state/app/modules/marianumMessage/view/marianum_message_list_view.dart +++ b/lib/state/app/modules/marianumMessage/view/marianum_message_list_view.dart @@ -1,12 +1,14 @@ import 'dart:developer'; import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; import '../../../../../api/mhsl/message/getMessages/getMessagesResponse.dart'; import '../../../../../view/pages/more/message/messageView.dart'; import '../../../infrastructure/loadableState/loadable_state.dart'; import '../../../infrastructure/loadableState/view/loadable_state_consumer.dart'; -import '../../../infrastructure/utilityWidgets/bloc_providing_builder.dart'; +import '../../../infrastructure/utilityWidgets/bloc_module.dart'; +import '../../../infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc_event.dart'; import '../bloc/marianum_message_bloc.dart'; import '../bloc/marianum_message_state.dart'; @@ -14,36 +16,41 @@ class MarianumMessageListView extends StatelessWidget { const MarianumMessageListView({super.key}); @override - Widget build(BuildContext context) => Scaffold( - appBar: AppBar( - title: const Text('Marianum Message'), - ), - body: BlocProvidingBuilder>( - create: (context) => MarianumMessageBloc(), - child: (context, state) { - // if(value.primaryLoading()) return const LoadingSpinner(); - log(state.toString()); - return LoadableStateConsumer, MarianumMessageState>( - child: (state) => ListView.builder( - itemCount: state.messageList.messages.length, - itemBuilder: (context, index) { - var message = state.messageList.messages.toList()[index]; - return ListTile( - leading: const Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [Icon(Icons.newspaper)], - ), - title: Text(message.name, overflow: TextOverflow.ellipsis), - subtitle: Text('vom ${message.date}'), - trailing: const Icon(Icons.arrow_right), - onTap: () { - Navigator.push(context, MaterialPageRoute(builder: (context) => MessageView(basePath: state.messageList.base, message: message as GetMessagesResponseObject))); - }, - ); - } + Widget build(BuildContext context) => BlocModule>( + create: (context) => MarianumMessageBloc(), + child: (context, state) { + // if(value.primaryLoading()) return const LoadingSpinner(); + log(state.toString()); + return Scaffold( + appBar: AppBar( + title: const Text('Marianum Message'), + actions: [ + IconButton(onPressed: () => context.read().add(MessageEvent()), icon: Icon(Icons.abc)), + IconButton(onPressed: () => context.read().add(Emit((state) => MarianumMessageState(messageList: MarianumMessageList(base: "", messages: [MarianumMessage(url: "", name: "Teeest", date: "now")])))), icon: Icon(Icons.add)), + IconButton(onPressed: () => context.read().add(ClearState()), icon: Icon(Icons.add)) + ], + ), + body: LoadableStateConsumer( + child: (state, loading) => ListView.builder( + itemCount: state.messageList.messages.length, + itemBuilder: (context, index) { + var message = state.messageList.messages.toList()[index]; + return ListTile( + leading: const Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [Icon(Icons.newspaper)], + ), + title: Text("${message.name}${loading ? "loading" : "notloading"}", overflow: TextOverflow.ellipsis), + subtitle: Text('vom ${message.date}'), + trailing: const Icon(Icons.arrow_right), + onTap: () { + Navigator.push(context, MaterialPageRoute(builder: (context) => MessageView(basePath: state.messageList.base, message: message as GetMessagesResponseObject))); + }, + ); + } ), - ); - } - ), + ), + ); + } ); }