From e25c67637934446da36d1bb581fe4dc28ec16cd4 Mon Sep 17 00:00:00 2001 From: Shadd <7162036-Shadord@users.noreply.gitlab.com> Date: Sun, 27 Nov 2022 18:15:18 +0100 Subject: [PATCH] feat(pageState): add Refs: #21 --- lib/common/page_state/page_state.dart | 32 + lib/common/page_state/page_state.freezed.dart | 1140 +++++++++++++++++ .../responsive_layout}/device_utils.dart | 59 +- .../responsive_layout/responsive_app.dart | 63 + .../versionable_bloc/versionable_bloc.dart | 2 + .../versionable_hydrated_bloc.dart | 6 + .../versionable_hydrated_bloc_mixin.dart} | 2 +- .../versionable_hydrated_cubit.dart | 6 + .../versionnable_bloc/versionnable_bloc.dart | 2 - .../versionnable_hydrated_bloc.dart | 6 - .../versionnable_hydrated_cubit.dart | 6 - lib/presentation/app/app.dart | 73 +- lib/presentation/home/home_page.dart | 2 +- lib/presentation/theme/bloc/theme_bloc.dart | 4 +- storybook/{ => lib}/app_storybook.dart | 3 + .../responsive_app_story.dart | 35 + storybook/main_storybook.dart | 2 +- .../common/page_state/page_state_test.dart | 14 + 18 files changed, 1400 insertions(+), 57 deletions(-) create mode 100644 lib/common/page_state/page_state.dart create mode 100644 lib/common/page_state/page_state.freezed.dart rename lib/{utils => common/responsive_layout}/device_utils.dart (69%) create mode 100644 lib/common/responsive_layout/responsive_app.dart create mode 100644 lib/common/versionable_bloc/versionable_bloc.dart create mode 100644 lib/common/versionable_bloc/versionable_hydrated_bloc.dart rename lib/common/{versionnable_bloc/versionnable_hydrated_bloc_mixin.dart => versionable_bloc/versionable_hydrated_bloc_mixin.dart} (94%) create mode 100644 lib/common/versionable_bloc/versionable_hydrated_cubit.dart delete mode 100644 lib/common/versionnable_bloc/versionnable_bloc.dart delete mode 100644 lib/common/versionnable_bloc/versionnable_hydrated_bloc.dart delete mode 100644 lib/common/versionnable_bloc/versionnable_hydrated_cubit.dart rename storybook/{ => lib}/app_storybook.dart (89%) create mode 100644 storybook/lib/common/responsive_layout/responsive_app_story.dart create mode 100644 tests/lib/common/page_state/page_state_test.dart diff --git a/lib/common/page_state/page_state.dart b/lib/common/page_state/page_state.dart new file mode 100644 index 0000000..2a0b072 --- /dev/null +++ b/lib/common/page_state/page_state.dart @@ -0,0 +1,32 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; +part 'page_state.freezed.dart'; + +typedef SimplePageState = PageState; + +/// A kind of PageState where you already have a previous data, before loading the page +typedef HydratedPageState = PageState; + +@freezed +class PageState with _$PageState { + const PageState._(); + + factory PageState.idle([Tidle? data]) = _PageStateIdle; + + factory PageState.loading([Tloading? data]) = _PageStateLoading; + + factory PageState.loaded({required T data}) = _PageStateLoaded; + + factory PageState.empty({Tempty? data}) = _PageStateEmpty; + + factory PageState.failure([Tfailure? data]) = _PageStateFailure; + + bool get isIdle => this is _PageStateIdle; + + bool get isLoading => this is _PageStateLoading; + + bool get isLoaded => this is _PageStateLoaded; + + bool get isEmpty => this is _PageStateEmpty; + + bool get isFailure => this is _PageStateFailure; +} diff --git a/lib/common/page_state/page_state.freezed.dart b/lib/common/page_state/page_state.freezed.dart new file mode 100644 index 0000000..07c24ab --- /dev/null +++ b/lib/common/page_state/page_state.freezed.dart @@ -0,0 +1,1140 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target + +part of 'page_state.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); + +/// @nodoc +mixin _$PageState { + @optionalTypeArgs + TResult when({ + required TResult Function(Tidle? data) idle, + required TResult Function(Tloading? data) loading, + required TResult Function(T data) loaded, + required TResult Function(Tempty? data) empty, + required TResult Function(Tfailure? data) failure, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function(Tidle? data)? idle, + TResult? Function(Tloading? data)? loading, + TResult? Function(T data)? loaded, + TResult? Function(Tempty? data)? empty, + TResult? Function(Tfailure? data)? failure, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeWhen({ + TResult Function(Tidle? data)? idle, + TResult Function(Tloading? data)? loading, + TResult Function(T data)? loaded, + TResult Function(Tempty? data)? empty, + TResult Function(Tfailure? data)? failure, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult map({ + required TResult Function( + _PageStateIdle value) + idle, + required TResult Function( + _PageStateLoading value) + loading, + required TResult Function( + _PageStateLoaded value) + loaded, + required TResult Function( + _PageStateEmpty value) + empty, + required TResult Function( + _PageStateFailure value) + failure, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function( + _PageStateIdle value)? + idle, + TResult? Function( + _PageStateLoading value)? + loading, + TResult? Function( + _PageStateLoaded value)? + loaded, + TResult? Function( + _PageStateEmpty value)? + empty, + TResult? Function( + _PageStateFailure value)? + failure, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeMap({ + TResult Function( + _PageStateIdle value)? + idle, + TResult Function( + _PageStateLoading value)? + loading, + TResult Function( + _PageStateLoaded value)? + loaded, + TResult Function( + _PageStateEmpty value)? + empty, + TResult Function( + _PageStateFailure value)? + failure, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $PageStateCopyWith { + factory $PageStateCopyWith( + PageState value, + $Res Function(PageState) then) = + _$PageStateCopyWithImpl>; +} + +/// @nodoc +class _$PageStateCopyWithImpl> + implements $PageStateCopyWith { + _$PageStateCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; +} + +/// @nodoc +abstract class _$$_PageStateIdleCopyWith { + factory _$$_PageStateIdleCopyWith( + _$_PageStateIdle value, + $Res Function(_$_PageStateIdle) + then) = __$$_PageStateIdleCopyWithImpl; + @useResult + $Res call({Tidle? data}); +} + +/// @nodoc +class __$$_PageStateIdleCopyWithImpl + extends _$PageStateCopyWithImpl> + implements + _$$_PageStateIdleCopyWith { + __$$_PageStateIdleCopyWithImpl( + _$_PageStateIdle _value, + $Res Function(_$_PageStateIdle) + _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? data = freezed, + }) { + return _then(_$_PageStateIdle( + freezed == data + ? _value.data + : data // ignore: cast_nullable_to_non_nullable + as Tidle?, + )); + } +} + +/// @nodoc + +class _$_PageStateIdle + extends _PageStateIdle { + _$_PageStateIdle([this.data]) : super._(); + + @override + final Tidle? data; + + @override + String toString() { + return 'PageState<$T, $Tidle, $Tloading, $Tempty, $Tfailure>.idle(data: $data)'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$_PageStateIdle && + const DeepCollectionEquality().equals(other.data, data)); + } + + @override + int get hashCode => + Object.hash(runtimeType, const DeepCollectionEquality().hash(data)); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$_PageStateIdleCopyWith> + get copyWith => __$$_PageStateIdleCopyWithImpl>( + this, _$identity); + + @override + @optionalTypeArgs + TResult when({ + required TResult Function(Tidle? data) idle, + required TResult Function(Tloading? data) loading, + required TResult Function(T data) loaded, + required TResult Function(Tempty? data) empty, + required TResult Function(Tfailure? data) failure, + }) { + return idle(data); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function(Tidle? data)? idle, + TResult? Function(Tloading? data)? loading, + TResult? Function(T data)? loaded, + TResult? Function(Tempty? data)? empty, + TResult? Function(Tfailure? data)? failure, + }) { + return idle?.call(data); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function(Tidle? data)? idle, + TResult Function(Tloading? data)? loading, + TResult Function(T data)? loaded, + TResult Function(Tempty? data)? empty, + TResult Function(Tfailure? data)? failure, + required TResult orElse(), + }) { + if (idle != null) { + return idle(data); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function( + _PageStateIdle value) + idle, + required TResult Function( + _PageStateLoading value) + loading, + required TResult Function( + _PageStateLoaded value) + loaded, + required TResult Function( + _PageStateEmpty value) + empty, + required TResult Function( + _PageStateFailure value) + failure, + }) { + return idle(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function( + _PageStateIdle value)? + idle, + TResult? Function( + _PageStateLoading value)? + loading, + TResult? Function( + _PageStateLoaded value)? + loaded, + TResult? Function( + _PageStateEmpty value)? + empty, + TResult? Function( + _PageStateFailure value)? + failure, + }) { + return idle?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function( + _PageStateIdle value)? + idle, + TResult Function( + _PageStateLoading value)? + loading, + TResult Function( + _PageStateLoaded value)? + loaded, + TResult Function( + _PageStateEmpty value)? + empty, + TResult Function( + _PageStateFailure value)? + failure, + required TResult orElse(), + }) { + if (idle != null) { + return idle(this); + } + return orElse(); + } +} + +abstract class _PageStateIdle + extends PageState { + factory _PageStateIdle([final Tidle? data]) = + _$_PageStateIdle; + _PageStateIdle._() : super._(); + + Tidle? get data; + @JsonKey(ignore: true) + _$$_PageStateIdleCopyWith> + get copyWith => throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class _$$_PageStateLoadingCopyWith { + factory _$$_PageStateLoadingCopyWith( + _$_PageStateLoading value, + $Res Function(_$_PageStateLoading) + then) = __$$_PageStateLoadingCopyWithImpl; + @useResult + $Res call({Tloading? data}); +} + +/// @nodoc +class __$$_PageStateLoadingCopyWithImpl + extends _$PageStateCopyWithImpl> + implements + _$$_PageStateLoadingCopyWith { + __$$_PageStateLoadingCopyWithImpl( + _$_PageStateLoading _value, + $Res Function(_$_PageStateLoading) + _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? data = freezed, + }) { + return _then(_$_PageStateLoading( + freezed == data + ? _value.data + : data // ignore: cast_nullable_to_non_nullable + as Tloading?, + )); + } +} + +/// @nodoc + +class _$_PageStateLoading + extends _PageStateLoading { + _$_PageStateLoading([this.data]) : super._(); + + @override + final Tloading? data; + + @override + String toString() { + return 'PageState<$T, $Tidle, $Tloading, $Tempty, $Tfailure>.loading(data: $data)'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other + is _$_PageStateLoading && + const DeepCollectionEquality().equals(other.data, data)); + } + + @override + int get hashCode => + Object.hash(runtimeType, const DeepCollectionEquality().hash(data)); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$_PageStateLoadingCopyWith> + get copyWith => __$$_PageStateLoadingCopyWithImpl< + T, + Tidle, + Tloading, + Tempty, + Tfailure, + _$_PageStateLoading>( + this, _$identity); + + @override + @optionalTypeArgs + TResult when({ + required TResult Function(Tidle? data) idle, + required TResult Function(Tloading? data) loading, + required TResult Function(T data) loaded, + required TResult Function(Tempty? data) empty, + required TResult Function(Tfailure? data) failure, + }) { + return loading(data); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function(Tidle? data)? idle, + TResult? Function(Tloading? data)? loading, + TResult? Function(T data)? loaded, + TResult? Function(Tempty? data)? empty, + TResult? Function(Tfailure? data)? failure, + }) { + return loading?.call(data); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function(Tidle? data)? idle, + TResult Function(Tloading? data)? loading, + TResult Function(T data)? loaded, + TResult Function(Tempty? data)? empty, + TResult Function(Tfailure? data)? failure, + required TResult orElse(), + }) { + if (loading != null) { + return loading(data); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function( + _PageStateIdle value) + idle, + required TResult Function( + _PageStateLoading value) + loading, + required TResult Function( + _PageStateLoaded value) + loaded, + required TResult Function( + _PageStateEmpty value) + empty, + required TResult Function( + _PageStateFailure value) + failure, + }) { + return loading(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function( + _PageStateIdle value)? + idle, + TResult? Function( + _PageStateLoading value)? + loading, + TResult? Function( + _PageStateLoaded value)? + loaded, + TResult? Function( + _PageStateEmpty value)? + empty, + TResult? Function( + _PageStateFailure value)? + failure, + }) { + return loading?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function( + _PageStateIdle value)? + idle, + TResult Function( + _PageStateLoading value)? + loading, + TResult Function( + _PageStateLoaded value)? + loaded, + TResult Function( + _PageStateEmpty value)? + empty, + TResult Function( + _PageStateFailure value)? + failure, + required TResult orElse(), + }) { + if (loading != null) { + return loading(this); + } + return orElse(); + } +} + +abstract class _PageStateLoading + extends PageState { + factory _PageStateLoading([final Tloading? data]) = + _$_PageStateLoading; + _PageStateLoading._() : super._(); + + Tloading? get data; + @JsonKey(ignore: true) + _$$_PageStateLoadingCopyWith> + get copyWith => throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class _$$_PageStateLoadedCopyWith { + factory _$$_PageStateLoadedCopyWith( + _$_PageStateLoaded value, + $Res Function(_$_PageStateLoaded) + then) = __$$_PageStateLoadedCopyWithImpl; + @useResult + $Res call({T data}); +} + +/// @nodoc +class __$$_PageStateLoadedCopyWithImpl + extends _$PageStateCopyWithImpl> + implements + _$$_PageStateLoadedCopyWith { + __$$_PageStateLoadedCopyWithImpl( + _$_PageStateLoaded _value, + $Res Function(_$_PageStateLoaded) + _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? data = null, + }) { + return _then(_$_PageStateLoaded( + data: null == data + ? _value.data + : data // ignore: cast_nullable_to_non_nullable + as T, + )); + } +} + +/// @nodoc + +class _$_PageStateLoaded + extends _PageStateLoaded { + _$_PageStateLoaded({required this.data}) : super._(); + + @override + final T data; + + @override + String toString() { + return 'PageState<$T, $Tidle, $Tloading, $Tempty, $Tfailure>.loaded(data: $data)'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$_PageStateLoaded && + const DeepCollectionEquality().equals(other.data, data)); + } + + @override + int get hashCode => + Object.hash(runtimeType, const DeepCollectionEquality().hash(data)); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$_PageStateLoadedCopyWith> + get copyWith => __$$_PageStateLoadedCopyWithImpl< + T, + Tidle, + Tloading, + Tempty, + Tfailure, + _$_PageStateLoaded>( + this, _$identity); + + @override + @optionalTypeArgs + TResult when({ + required TResult Function(Tidle? data) idle, + required TResult Function(Tloading? data) loading, + required TResult Function(T data) loaded, + required TResult Function(Tempty? data) empty, + required TResult Function(Tfailure? data) failure, + }) { + return loaded(data); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function(Tidle? data)? idle, + TResult? Function(Tloading? data)? loading, + TResult? Function(T data)? loaded, + TResult? Function(Tempty? data)? empty, + TResult? Function(Tfailure? data)? failure, + }) { + return loaded?.call(data); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function(Tidle? data)? idle, + TResult Function(Tloading? data)? loading, + TResult Function(T data)? loaded, + TResult Function(Tempty? data)? empty, + TResult Function(Tfailure? data)? failure, + required TResult orElse(), + }) { + if (loaded != null) { + return loaded(data); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function( + _PageStateIdle value) + idle, + required TResult Function( + _PageStateLoading value) + loading, + required TResult Function( + _PageStateLoaded value) + loaded, + required TResult Function( + _PageStateEmpty value) + empty, + required TResult Function( + _PageStateFailure value) + failure, + }) { + return loaded(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function( + _PageStateIdle value)? + idle, + TResult? Function( + _PageStateLoading value)? + loading, + TResult? Function( + _PageStateLoaded value)? + loaded, + TResult? Function( + _PageStateEmpty value)? + empty, + TResult? Function( + _PageStateFailure value)? + failure, + }) { + return loaded?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function( + _PageStateIdle value)? + idle, + TResult Function( + _PageStateLoading value)? + loading, + TResult Function( + _PageStateLoaded value)? + loaded, + TResult Function( + _PageStateEmpty value)? + empty, + TResult Function( + _PageStateFailure value)? + failure, + required TResult orElse(), + }) { + if (loaded != null) { + return loaded(this); + } + return orElse(); + } +} + +abstract class _PageStateLoaded + extends PageState { + factory _PageStateLoaded({required final T data}) = + _$_PageStateLoaded; + _PageStateLoaded._() : super._(); + + T get data; + @JsonKey(ignore: true) + _$$_PageStateLoadedCopyWith> + get copyWith => throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class _$$_PageStateEmptyCopyWith { + factory _$$_PageStateEmptyCopyWith( + _$_PageStateEmpty value, + $Res Function(_$_PageStateEmpty) + then) = __$$_PageStateEmptyCopyWithImpl; + @useResult + $Res call({Tempty? data}); +} + +/// @nodoc +class __$$_PageStateEmptyCopyWithImpl + extends _$PageStateCopyWithImpl> + implements + _$$_PageStateEmptyCopyWith { + __$$_PageStateEmptyCopyWithImpl( + _$_PageStateEmpty _value, + $Res Function(_$_PageStateEmpty) + _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? data = freezed, + }) { + return _then(_$_PageStateEmpty( + data: freezed == data + ? _value.data + : data // ignore: cast_nullable_to_non_nullable + as Tempty?, + )); + } +} + +/// @nodoc + +class _$_PageStateEmpty + extends _PageStateEmpty { + _$_PageStateEmpty({this.data}) : super._(); + + @override + final Tempty? data; + + @override + String toString() { + return 'PageState<$T, $Tidle, $Tloading, $Tempty, $Tfailure>.empty(data: $data)'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$_PageStateEmpty && + const DeepCollectionEquality().equals(other.data, data)); + } + + @override + int get hashCode => + Object.hash(runtimeType, const DeepCollectionEquality().hash(data)); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$_PageStateEmptyCopyWith> + get copyWith => __$$_PageStateEmptyCopyWithImpl< + T, + Tidle, + Tloading, + Tempty, + Tfailure, + _$_PageStateEmpty>( + this, _$identity); + + @override + @optionalTypeArgs + TResult when({ + required TResult Function(Tidle? data) idle, + required TResult Function(Tloading? data) loading, + required TResult Function(T data) loaded, + required TResult Function(Tempty? data) empty, + required TResult Function(Tfailure? data) failure, + }) { + return empty(data); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function(Tidle? data)? idle, + TResult? Function(Tloading? data)? loading, + TResult? Function(T data)? loaded, + TResult? Function(Tempty? data)? empty, + TResult? Function(Tfailure? data)? failure, + }) { + return empty?.call(data); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function(Tidle? data)? idle, + TResult Function(Tloading? data)? loading, + TResult Function(T data)? loaded, + TResult Function(Tempty? data)? empty, + TResult Function(Tfailure? data)? failure, + required TResult orElse(), + }) { + if (empty != null) { + return empty(data); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function( + _PageStateIdle value) + idle, + required TResult Function( + _PageStateLoading value) + loading, + required TResult Function( + _PageStateLoaded value) + loaded, + required TResult Function( + _PageStateEmpty value) + empty, + required TResult Function( + _PageStateFailure value) + failure, + }) { + return empty(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function( + _PageStateIdle value)? + idle, + TResult? Function( + _PageStateLoading value)? + loading, + TResult? Function( + _PageStateLoaded value)? + loaded, + TResult? Function( + _PageStateEmpty value)? + empty, + TResult? Function( + _PageStateFailure value)? + failure, + }) { + return empty?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function( + _PageStateIdle value)? + idle, + TResult Function( + _PageStateLoading value)? + loading, + TResult Function( + _PageStateLoaded value)? + loaded, + TResult Function( + _PageStateEmpty value)? + empty, + TResult Function( + _PageStateFailure value)? + failure, + required TResult orElse(), + }) { + if (empty != null) { + return empty(this); + } + return orElse(); + } +} + +abstract class _PageStateEmpty + extends PageState { + factory _PageStateEmpty({final Tempty? data}) = + _$_PageStateEmpty; + _PageStateEmpty._() : super._(); + + Tempty? get data; + @JsonKey(ignore: true) + _$$_PageStateEmptyCopyWith> + get copyWith => throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class _$$_PageStateFailureCopyWith { + factory _$$_PageStateFailureCopyWith( + _$_PageStateFailure value, + $Res Function(_$_PageStateFailure) + then) = __$$_PageStateFailureCopyWithImpl; + @useResult + $Res call({Tfailure? data}); +} + +/// @nodoc +class __$$_PageStateFailureCopyWithImpl + extends _$PageStateCopyWithImpl> + implements + _$$_PageStateFailureCopyWith { + __$$_PageStateFailureCopyWithImpl( + _$_PageStateFailure _value, + $Res Function(_$_PageStateFailure) + _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? data = freezed, + }) { + return _then(_$_PageStateFailure( + freezed == data + ? _value.data + : data // ignore: cast_nullable_to_non_nullable + as Tfailure?, + )); + } +} + +/// @nodoc + +class _$_PageStateFailure + extends _PageStateFailure { + _$_PageStateFailure([this.data]) : super._(); + + @override + final Tfailure? data; + + @override + String toString() { + return 'PageState<$T, $Tidle, $Tloading, $Tempty, $Tfailure>.failure(data: $data)'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other + is _$_PageStateFailure && + const DeepCollectionEquality().equals(other.data, data)); + } + + @override + int get hashCode => + Object.hash(runtimeType, const DeepCollectionEquality().hash(data)); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$_PageStateFailureCopyWith> + get copyWith => __$$_PageStateFailureCopyWithImpl< + T, + Tidle, + Tloading, + Tempty, + Tfailure, + _$_PageStateFailure>( + this, _$identity); + + @override + @optionalTypeArgs + TResult when({ + required TResult Function(Tidle? data) idle, + required TResult Function(Tloading? data) loading, + required TResult Function(T data) loaded, + required TResult Function(Tempty? data) empty, + required TResult Function(Tfailure? data) failure, + }) { + return failure(data); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function(Tidle? data)? idle, + TResult? Function(Tloading? data)? loading, + TResult? Function(T data)? loaded, + TResult? Function(Tempty? data)? empty, + TResult? Function(Tfailure? data)? failure, + }) { + return failure?.call(data); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function(Tidle? data)? idle, + TResult Function(Tloading? data)? loading, + TResult Function(T data)? loaded, + TResult Function(Tempty? data)? empty, + TResult Function(Tfailure? data)? failure, + required TResult orElse(), + }) { + if (failure != null) { + return failure(data); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function( + _PageStateIdle value) + idle, + required TResult Function( + _PageStateLoading value) + loading, + required TResult Function( + _PageStateLoaded value) + loaded, + required TResult Function( + _PageStateEmpty value) + empty, + required TResult Function( + _PageStateFailure value) + failure, + }) { + return failure(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function( + _PageStateIdle value)? + idle, + TResult? Function( + _PageStateLoading value)? + loading, + TResult? Function( + _PageStateLoaded value)? + loaded, + TResult? Function( + _PageStateEmpty value)? + empty, + TResult? Function( + _PageStateFailure value)? + failure, + }) { + return failure?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function( + _PageStateIdle value)? + idle, + TResult Function( + _PageStateLoading value)? + loading, + TResult Function( + _PageStateLoaded value)? + loaded, + TResult Function( + _PageStateEmpty value)? + empty, + TResult Function( + _PageStateFailure value)? + failure, + required TResult orElse(), + }) { + if (failure != null) { + return failure(this); + } + return orElse(); + } +} + +abstract class _PageStateFailure + extends PageState { + factory _PageStateFailure([final Tfailure? data]) = + _$_PageStateFailure; + _PageStateFailure._() : super._(); + + Tfailure? get data; + @JsonKey(ignore: true) + _$$_PageStateFailureCopyWith> + get copyWith => throw _privateConstructorUsedError; +} diff --git a/lib/utils/device_utils.dart b/lib/common/responsive_layout/device_utils.dart similarity index 69% rename from lib/utils/device_utils.dart rename to lib/common/responsive_layout/device_utils.dart index 43b192f..6f280e8 100644 --- a/lib/utils/device_utils.dart +++ b/lib/common/responsive_layout/device_utils.dart @@ -13,7 +13,7 @@ enum DeviceType { android, ios, fuchsia, web, windows, mac, linux } /// This can either be mobile or tablet enum ScreenType { mobile, tablet, desktop } -class CurrentDevice { +abstract class CurrentDevice { /// Device's BoxConstraints static late BoxConstraints boxConstraints; @@ -40,12 +40,12 @@ class CurrentDevice { /// Sets the Screen's size and Device's `Orientation`, /// `BoxConstraints`, `Height`, and `Width` - static void setScreenSize( - BoxConstraints constraints, - Orientation currentOrientation, - double maxMobileWidth, [ + static void setScreenSize({ + required BoxConstraints constraints, + required Orientation currentOrientation, + required double maxMobileWidth, double? maxTabletWidth, - ]) { + }) { // Sets boxconstraints and orientation boxConstraints = constraints; orientation = currentOrientation; @@ -96,4 +96,51 @@ class CurrentDevice { screenType = ScreenType.desktop; } } + + static Widget map({ + required Widget Function() sm, + required Widget Function() md, + required Widget Function() lg, + required Widget Function() xl, + required Widget Function() xl2, + }) { + if (width < 640) { + return sm(); + } + if (width < 768) { + return md(); + } + if (width < 1024) { + return lg(); + } + if (width < 1280) { + return xl(); + } + // width < 1536 + return xl2(); + } + + static Widget maybeMap({ + required Widget Function() orElse, + Widget Function()? sm, + Widget Function()? md, + Widget Function()? lg, + Widget Function()? xl, + Widget Function()? xl2, + }) { + if (width < 640) { + return sm != null ? sm() : orElse(); + } + if (width < 768) { + return md != null ? md() : orElse(); + } + if (width < 1024) { + return lg != null ? lg() : orElse(); + } + if (width < 1280) { + return xl != null ? xl() : orElse(); + } + // width < 1536 + return xl2 != null ? xl2() : orElse(); + } } diff --git a/lib/common/responsive_layout/responsive_app.dart b/lib/common/responsive_layout/responsive_app.dart new file mode 100644 index 0000000..33062d4 --- /dev/null +++ b/lib/common/responsive_layout/responsive_app.dart @@ -0,0 +1,63 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_devtools/common/responsive_layout/device_utils.dart'; + +/// @Use from responsive_sizer https://github.com/CoderUni/responsive_sizer + +typedef ResponsiveBuilderType = Widget Function( + BuildContext, + Orientation, + ScreenType, +); + +class ResponsiveApp extends StatelessWidget { + const ResponsiveApp({ + required this.builder, + this.maxMobileWidth = 599, + this.maxTabletWidth, + Key? key, + }) : super(key: key); + + /// Builds the widget whenever the orientation changes + final ResponsiveBuilderType builder; + + /// This is the breakpoint used to determine whether the device is + /// a mobile device or a tablet. + /// + /// If the `MediaQuery`'s width **in portrait mode** is less than or equal + /// to `maxMobileWidth`, the device is in a mobile device + final double maxMobileWidth; + + /// By default, the `ScreenType` can only be mobile or tablet. If this is set, + /// the `ScreenType` can be desktop as well + /// + /// This is the breakpoint used to determine whether the device is + /// a tablet or a desktop. + /// + /// If the `MediaQuery`'s width **in portrait mode** is + /// less than or equal to `maxTabletWidth`, the device is in a tablet device + final double? maxTabletWidth; + + @override + Widget build(BuildContext context) { + return LayoutBuilder(builder: (context, constraints) { + return OrientationBuilder(builder: (context, orientation) { + + CurrentDevice.setScreenSize( + constraints: constraints, + currentOrientation: orientation, + maxMobileWidth: maxMobileWidth, + maxTabletWidth: maxTabletWidth, + ); + + if (constraints.maxWidth == 0 || constraints.maxHeight == 0) { + return const SizedBox(); + } + return builder( + context, + orientation, + CurrentDevice.screenType, + ); + }); + }); + } +} diff --git a/lib/common/versionable_bloc/versionable_bloc.dart b/lib/common/versionable_bloc/versionable_bloc.dart new file mode 100644 index 0000000..17b4bb5 --- /dev/null +++ b/lib/common/versionable_bloc/versionable_bloc.dart @@ -0,0 +1,2 @@ +export 'versionable_hydrated_bloc.dart'; +export 'versionable_hydrated_cubit.dart'; diff --git a/lib/common/versionable_bloc/versionable_hydrated_bloc.dart b/lib/common/versionable_bloc/versionable_hydrated_bloc.dart new file mode 100644 index 0000000..831f8c0 --- /dev/null +++ b/lib/common/versionable_bloc/versionable_hydrated_bloc.dart @@ -0,0 +1,6 @@ +import 'package:flutter_devtools/common/versionable_bloc/versionable_hydrated_bloc_mixin.dart'; +import 'package:hydrated_bloc/hydrated_bloc.dart'; + +abstract class VersionableHydratedBloc extends Bloc with HydratedMixin, VersionableHydratedBlocMixin { + VersionableHydratedBloc(U state) : super(state); +} diff --git a/lib/common/versionnable_bloc/versionnable_hydrated_bloc_mixin.dart b/lib/common/versionable_bloc/versionable_hydrated_bloc_mixin.dart similarity index 94% rename from lib/common/versionnable_bloc/versionnable_hydrated_bloc_mixin.dart rename to lib/common/versionable_bloc/versionable_hydrated_bloc_mixin.dart index af4b1d2..0cfe7e9 100644 --- a/lib/common/versionnable_bloc/versionnable_hydrated_bloc_mixin.dart +++ b/lib/common/versionable_bloc/versionable_hydrated_bloc_mixin.dart @@ -1,6 +1,6 @@ import 'package:hydrated_bloc/hydrated_bloc.dart'; -mixin VersionnableHydratedBlocMixin on HydratedMixin { +mixin VersionableHydratedBlocMixin on HydratedMixin { static const _storageKey = 'all_bloc_version'; /// The version of the bloc represent the version of the state data model diff --git a/lib/common/versionable_bloc/versionable_hydrated_cubit.dart b/lib/common/versionable_bloc/versionable_hydrated_cubit.dart new file mode 100644 index 0000000..861d1b2 --- /dev/null +++ b/lib/common/versionable_bloc/versionable_hydrated_cubit.dart @@ -0,0 +1,6 @@ +import 'package:flutter_devtools/common/versionable_bloc/versionable_hydrated_bloc_mixin.dart'; +import 'package:hydrated_bloc/hydrated_bloc.dart'; + +abstract class VersionableHydratedCubit extends Cubit with HydratedMixin, VersionableHydratedBlocMixin { + VersionableHydratedCubit(T state) : super(state); +} diff --git a/lib/common/versionnable_bloc/versionnable_bloc.dart b/lib/common/versionnable_bloc/versionnable_bloc.dart deleted file mode 100644 index 6775641..0000000 --- a/lib/common/versionnable_bloc/versionnable_bloc.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'versionnable_hydrated_bloc.dart'; -export 'versionnable_hydrated_cubit.dart'; diff --git a/lib/common/versionnable_bloc/versionnable_hydrated_bloc.dart b/lib/common/versionnable_bloc/versionnable_hydrated_bloc.dart deleted file mode 100644 index a4ca2f3..0000000 --- a/lib/common/versionnable_bloc/versionnable_hydrated_bloc.dart +++ /dev/null @@ -1,6 +0,0 @@ -import 'package:flutter_devtools/common/versionnable_bloc/versionnable_hydrated_bloc_mixin.dart'; -import 'package:hydrated_bloc/hydrated_bloc.dart'; - -abstract class VersionnableHydratedBloc extends Bloc with HydratedMixin, VersionnableHydratedBlocMixin { - VersionnableHydratedBloc(U state) : super(state); -} diff --git a/lib/common/versionnable_bloc/versionnable_hydrated_cubit.dart b/lib/common/versionnable_bloc/versionnable_hydrated_cubit.dart deleted file mode 100644 index 71e569a..0000000 --- a/lib/common/versionnable_bloc/versionnable_hydrated_cubit.dart +++ /dev/null @@ -1,6 +0,0 @@ -import 'package:flutter_devtools/common/versionnable_bloc/versionnable_hydrated_bloc_mixin.dart'; -import 'package:hydrated_bloc/hydrated_bloc.dart'; - -abstract class VersionnableHydratedCubit extends Cubit with HydratedMixin, VersionnableHydratedBlocMixin { - VersionnableHydratedCubit(T state) : super(state); -} diff --git a/lib/presentation/app/app.dart b/lib/presentation/app/app.dart index efed48f..06e0bc5 100644 --- a/lib/presentation/app/app.dart +++ b/lib/presentation/app/app.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_devtools/common/responsive_layout/responsive_app.dart'; import 'package:flutter_devtools/presentation/app/app_module.dart'; import 'package:flutter_devtools/presentation/theme/bloc/theme_bloc.dart'; import 'package:flutter_devtools/presentation/theme/bloc/theme_state.dart'; @@ -16,38 +17,46 @@ class App extends StatelessWidget { @override Widget build(BuildContext context) { - return ModularApp( - module: AppModule(themeBloc: _themeBloc), - child: BlocBuilder( - bloc: _themeBloc, - builder: (context, themeState) { - return MaterialApp.router( - debugShowCheckedModeBanner: false, - restorationScopeId: 'app', - localizationsDelegates: const [ - // AppLocalizations.delegate, - // GlobalMaterialLocalizations.delegate, - // GlobalWidgetsLocalizations.delegate, - // GlobalCupertinoLocalizations.delegate, - ], - supportedLocales: const [ - Locale('en', ''), // English, no country code - ], - onGenerateTitle: (context) => 'Flutter DevTools', - theme: AppTheme.build( - appColors: lightAppColors, - brightness: Brightness.light, - ), - darkTheme: AppTheme.build( - appColors: darkAppColors, - brightness: Brightness.dark, - ), - themeMode: themeState.selectedThemeMode, - routeInformationParser: Modular.routeInformationParser, - routerDelegate: Modular.routerDelegate, - ); - }, - ), + return ResponsiveApp( + builder: ( + context, + orientation, + type, + ) { + return ModularApp( + module: AppModule(themeBloc: _themeBloc), + child: BlocBuilder( + bloc: _themeBloc, + builder: (context, themeState) { + return MaterialApp.router( + debugShowCheckedModeBanner: false, + restorationScopeId: 'app', + localizationsDelegates: const [ + // AppLocalizations.delegate, + // GlobalMaterialLocalizations.delegate, + // GlobalWidgetsLocalizations.delegate, + // GlobalCupertinoLocalizations.delegate, + ], + supportedLocales: const [ + Locale('en', ''), // English, no country code + ], + onGenerateTitle: (context) => 'Flutter DevTools', + theme: AppTheme.build( + appColors: lightAppColors, + brightness: Brightness.light, + ), + darkTheme: AppTheme.build( + appColors: darkAppColors, + brightness: Brightness.dark, + ), + themeMode: themeState.selectedThemeMode, + routeInformationParser: Modular.routeInformationParser, + routerDelegate: Modular.routerDelegate, + ); + }, + ), + ); + }, ); } } diff --git a/lib/presentation/home/home_page.dart b/lib/presentation/home/home_page.dart index c97ed2c..f8916cd 100644 --- a/lib/presentation/home/home_page.dart +++ b/lib/presentation/home/home_page.dart @@ -12,7 +12,7 @@ class HomePage extends StatelessWidget { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: const Text('HomePage '), + title: const Text('HomePage'), ), body: Center( child: ElevatedButton( diff --git a/lib/presentation/theme/bloc/theme_bloc.dart b/lib/presentation/theme/bloc/theme_bloc.dart index 8afacaf..9c99487 100644 --- a/lib/presentation/theme/bloc/theme_bloc.dart +++ b/lib/presentation/theme/bloc/theme_bloc.dart @@ -2,7 +2,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; -import 'package:flutter_devtools/common/versionnable_bloc/versionnable_hydrated_bloc.dart'; +import 'package:flutter_devtools/common/versionable_bloc/versionable_hydrated_bloc.dart'; import 'package:flutter_devtools/presentation/theme/bloc/theme_events.dart'; import 'package:flutter_devtools/presentation/theme/bloc/theme_state.dart'; import 'package:hydrated_bloc/hydrated_bloc.dart'; @@ -12,7 +12,7 @@ ThemeMode get deviceBaseThemeMode => ? ThemeMode.dark : ThemeMode.light; -class ThemeBloc extends VersionnableHydratedBloc { +class ThemeBloc extends VersionableHydratedBloc { ThemeBloc() : super( ThemeState(selectedThemeMode: deviceBaseThemeMode), diff --git a/storybook/app_storybook.dart b/storybook/lib/app_storybook.dart similarity index 89% rename from storybook/app_storybook.dart rename to storybook/lib/app_storybook.dart index 03d07c8..fd02017 100644 --- a/storybook/app_storybook.dart +++ b/storybook/lib/app_storybook.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; import 'package:storybook_flutter/storybook_flutter.dart'; +import 'common/responsive_layout/responsive_app_story.dart'; + class AppStorybook extends StatelessWidget { const AppStorybook(); @@ -16,6 +18,7 @@ class AppStorybook extends StatelessWidget { return Storybook( plugins: plugins, stories: [ + ResponsiveAppStory(), Story( name: 'Widgets/Text', description: 'Simple text widget.', diff --git a/storybook/lib/common/responsive_layout/responsive_app_story.dart b/storybook/lib/common/responsive_layout/responsive_app_story.dart new file mode 100644 index 0000000..73b9bcc --- /dev/null +++ b/storybook/lib/common/responsive_layout/responsive_app_story.dart @@ -0,0 +1,35 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_devtools/common/responsive_layout/device_utils.dart'; +import 'package:flutter_devtools/common/responsive_layout/responsive_app.dart'; +import 'package:storybook_flutter/storybook_flutter.dart'; + +class ResponsiveAppStory extends Story { + ResponsiveAppStory() + : super( + name: 'Responsive App', + builder: (context) { + return ResponsiveApp( + builder: ( + context, + orientation, + type, + ) { + return SafeArea( + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text('Device Type : ${CurrentDevice.deviceType}'), + Text('Screen Type : ${type.name}'), + Text('Orientation : ${orientation.name}'), + Text('Constrains : Height = ${CurrentDevice.height} / Width = ${CurrentDevice.width}'), + Text('Constrains : AspectRatio = ${CurrentDevice.aspectRatio}'), + ], + ), + ), + ); + }, + ); + }, + ); +} diff --git a/storybook/main_storybook.dart b/storybook/main_storybook.dart index 249e913..839eec8 100644 --- a/storybook/main_storybook.dart +++ b/storybook/main_storybook.dart @@ -4,7 +4,7 @@ import 'package:hydrated_bloc/hydrated_bloc.dart'; import 'package:path_provider/path_provider.dart' as path_provider; import 'package:flutter_web_plugins/url_strategy.dart'; -import 'app_storybook.dart'; +import 'lib/app_storybook.dart'; void main() async { usePathUrlStrategy(); diff --git a/tests/lib/common/page_state/page_state_test.dart b/tests/lib/common/page_state/page_state_test.dart new file mode 100644 index 0000000..1ec7231 --- /dev/null +++ b/tests/lib/common/page_state/page_state_test.dart @@ -0,0 +1,14 @@ +import 'package:flutter_devtools/common/page_state/page_state.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('PageState', () { + test('it should return each types', () { + expect(PageState.empty().isEmpty, true); + expect(PageState.loaded(data: []).isLoaded, true); + expect(PageState.failure().isFailure, true); + expect(PageState.idle().isIdle, true); + expect(PageState.loading().isLoading, true); + }); + }); +}