loadable state is now detecting device connection status on failure

This commit is contained in:
2024-04-23 22:40:18 +02:00
parent 450c26b187
commit 04e8ce9c0a
10 changed files with 296 additions and 63 deletions

View File

@ -0,0 +1,21 @@
import 'package:flutter/material.dart';
class BackgroundLoadingIndicator extends StatelessWidget {
final bool visible;
const BackgroundLoadingIndicator({required this.visible, super.key});
final Duration animationDuration = const Duration(milliseconds: 200);
@override
Widget build(BuildContext context) => AnimatedSwitcher(
duration: animationDuration,
transitionBuilder: (Widget child, Animation<double> animation) => SlideTransition(
position: Tween<Offset>(
begin: const Offset(0.0, -1.0),
end: Offset.zero,
).animate(animation),
child: child,
),
child: visible ? const LinearProgressIndicator() : const SizedBox.shrink(),
);
}

View File

@ -0,0 +1,53 @@
import 'package:flutter/material.dart';
import '../../app/base/infrastructure/errorBar/error_bar_controller.dart';
import '../../infrastructure/state_extensions.dart';
import '../controller_provider.dart';
class ErrorBar extends StatelessWidget {
final bool visible;
const ErrorBar({required this.visible, super.key});
final Duration animationDuration = const Duration(milliseconds: 200);
@override
Widget build(BuildContext context) => ControllerProvider<ErrorBarController>(
create: (context) => ErrorBarController(),
child: (context) => AnimatedSwitcher(
duration: animationDuration,
transitionBuilder: (Widget child, Animation<double> animation) => SlideTransition(
position: Tween<Offset>(
begin: const Offset(0.0, -1.0),
end: Offset.zero,
).animate(animation),
child: child,
),
child: Visibility(
visible: visible,
child: Builder(
builder: (context) {
var controller = context.watchController<ErrorBarController>();
var status = controller.connectivityStatusKnown() && !controller.isConnected()
? (icon: Icons.wifi_off_outlined, text: 'Offline', color: Colors.grey.shade600)
: (icon: Icons.wifi_find_outlined, text: 'Verbindung fehlgeschlagen', color: Theme.of(context).primaryColor);
return Container(
height: 20,
decoration: BoxDecoration(
color: status.color,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(status.icon, size: 14),
const SizedBox(width: 10),
Text(status.text, style: const TextStyle(fontSize: 12))
],
),
);
},
)
)
),
);
}

View File

@ -0,0 +1,16 @@
import 'package:flutter/material.dart';
class PrimaryLoadingIndicator extends StatelessWidget {
final bool visible;
const PrimaryLoadingIndicator({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,
curve: Curves.easeInOut,
child: const Center(child: CircularProgressIndicator()),
);
}

View File

@ -3,6 +3,9 @@ import 'package:flutter/material.dart';
import '../infrastructure/controller.dart';
import '../infrastructure/loadable_state.dart';
import '../infrastructure/state_extensions.dart';
import 'components/background_loading_indicator.dart';
import 'components/error_bar.dart';
import 'components/primary_loading_indicator.dart';
class LoadableControllerConsumer<TController extends Controller<TState>, TState extends LoadableState> extends StatelessWidget {
final Widget child;
@ -13,69 +16,25 @@ class LoadableControllerConsumer<TController extends Controller<TState>, TState
@override
Widget build(BuildContext context) {
var state = context.readController<TController>().state;
var loadableContent = Stack(
return Column(
children: [
AnimatedOpacity(
opacity: !state.hasStateData() ? 1.0 : 0.0,
duration: animationDuration,
curve: Curves.easeInOut,
child: const Center(child: CircularProgressIndicator()),
),
AnimatedSwitcher(
duration: animationDuration,
transitionBuilder: (Widget child, Animation<double> animation) => SlideTransition(
position: Tween<Offset>(
begin: const Offset(0.0, -1.0),
end: Offset.zero,
).animate(animation),
child: child,
),
child: state.isBackgroundLoading() && !state.errorBarVisible() ? const LinearProgressIndicator() : const SizedBox.shrink(),
),
AnimatedOpacity(
opacity: state.hasStateData() ? 1.0 : 0.0,
duration: animationDuration,
curve: Curves.easeInOut,
child: state.hasStateData() ? child : const SizedBox.shrink()
),
],
);
var errorBar = AnimatedSwitcher(
duration: animationDuration,
transitionBuilder: (Widget child, Animation<double> animation) => SlideTransition(
position: Tween<Offset>(
begin: const Offset(0.0, -1.0),
end: Offset.zero,
).animate(animation),
child: child,
),
child: !state.errorBarVisible()
? const SizedBox.shrink()
: Container(
height: 20,
decoration: const BoxDecoration(
color: Colors.red,
),
child: const Row(
mainAxisAlignment: MainAxisAlignment.center,
ErrorBar(visible: state.errorBarVisible()),
Expanded(
child: Stack(
children: [
Icon(Icons.wifi_find_outlined, size: 12),
SizedBox(width: 10),
Text('Keine Verbindung', style: TextStyle(fontSize: 12))
PrimaryLoadingIndicator(visible: !state.hasStateData()),
BackgroundLoadingIndicator(visible: state.isBackgroundLoading() && !state.errorBarVisible()),
AnimatedOpacity(
opacity: state.hasStateData() ? 1.0 : 0.0,
duration: animationDuration,
curve: Curves.easeInOut,
child: state.hasStateData() ? child : const SizedBox.shrink()
),
],
),
),
);
return Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: [errorBar, loadableContent],
)
],
);
}
}

View File

@ -3,9 +3,9 @@ import 'package:flutter_bloc/flutter_bloc.dart';
class SubSelectedControllerConsumer<TController extends Cubit<TFullState>, TFullState, TFilteredState> extends StatelessWidget {
final Widget Function(BuildContext context, TFilteredState state) child;
final TFilteredState Function(TFullState state) subselect;
const SubSelectedControllerConsumer({required this.subselect, required this.child, super.key});
final TFilteredState Function(TFullState state) subSelect;
const SubSelectedControllerConsumer({required this.subSelect, required this.child, super.key});
@override
Widget build(BuildContext context) => BlocSelector<TController, TFullState, TFilteredState>(selector: subselect, builder: child);
Widget build(BuildContext context) => BlocSelector<TController, TFullState, TFilteredState>(selector: subSelect, builder: child);
}