Skip to content

Commit

Permalink
Added UI Animations and ScreenSize reestructure
Browse files Browse the repository at this point in the history
  • Loading branch information
hectorAguero committed Apr 17, 2024
1 parent ad8de91 commit 257a74f
Show file tree
Hide file tree
Showing 32 changed files with 683 additions and 431 deletions.
4 changes: 2 additions & 2 deletions lib/common_widgets/app_animation_wrapper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ class _AppAnimationWrapperState extends State<AppAnimationWrapper>
super.initState();
_controller = AnimationController(
vsync: this,
duration: const Duration(seconds: 1),
duration: const Duration(milliseconds: 350),
)..forward();
_animation = Tween<double>(begin: 0.5, end: 1).animate(
_animation = Tween<double>(begin: 0.6, end: 1).animate(
CurvedAnimation(
parent: _controller,
curve: Curves.easeInOut,
Expand Down
115 changes: 115 additions & 0 deletions lib/common_widgets/app_async_widget.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:sliver_tools/sliver_tools.dart';

import '../extensions/app_localization_extension.dart';
import 'app_cupertino_button.dart';

class AppAsyncWidget<T> extends StatelessWidget {
const AppAsyncWidget({
required this.asyncValue,
required this.child,
super.key,
this.onErrorRetry,
});

final AsyncValue<T> asyncValue;
final VoidCallback? onErrorRetry;
final Widget child;

@override
Widget build(BuildContext context) {
return switch (asyncValue) {
AsyncData() => child,
AsyncError(:final error) => Center(
key: const ValueKey('error'),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
error.toString(),
style: Theme.of(context).textTheme.titleLarge,
),
if (onErrorRetry != null)
CupertinoButton(
onPressed: onErrorRetry,
child: Text(context.loc.retry),
),
],
),
),
AsyncLoading() => const Center(
key: ValueKey('loading'),
child: Center(
child: SizedBox(
width: 32,
height: 32,
child: CircularProgressIndicator.adaptive(),
),
),
),
};
}
}

class AppAsyncSliver<T> extends StatelessWidget {
const AppAsyncSliver({
required this.asyncValue,
required this.child,
this.onErrorRetry,
super.key,
});

final AsyncValue<T> asyncValue;
final VoidCallback? onErrorRetry;
final Widget Function(T) child;

@override
Widget build(BuildContext context) {
return SliverAnimatedSwitcher(
duration: kThemeAnimationDuration,
child: switch (asyncValue) {
AsyncData(:final value) => child(value),
AsyncError(:final error) => SliverFillRemaining(
key: const ValueKey('error'),
child: Padding(
padding: const EdgeInsets.all(16),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
error.toString(),
style: Theme.of(context).textTheme.titleLarge,
textAlign: TextAlign.center,
),
if (onErrorRetry != null)
Padding(
padding: const EdgeInsets.only(top: 16),
child: AppCupertinoButton.tinted(
color: Theme.of(context).colorScheme.primary,
onPressed: onErrorRetry,
icon: const Icon(Icons.refresh),
child: Text(context.loc.retry),
),
),
],
),
),
),
),
AsyncLoading() => const SliverFillRemaining(
key: ValueKey('loading'),
child: Center(
child: SizedBox(
width: 32,
height: 32,
child: CircularProgressIndicator.adaptive(),
),
),
),
},
);
}
}
86 changes: 77 additions & 9 deletions lib/common_widgets/app_cupertino_button.dart
Original file line number Diff line number Diff line change
@@ -1,35 +1,103 @@
import 'package:flex_color_scheme/flex_color_scheme.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

import '../extensions/theme_of_context_extension.dart';

///extended CupertinoButton to pass null values in the minimumSize and padding
enum CupertinoButtonType {
plain,
gray,
tinted,
filled,
}

class AppCupertinoButton extends StatelessWidget {
const AppCupertinoButton({
required this.child,
required this.onPressed,
super.key,
this.color,
this.disabledColor,
this.padding,
this.icon,
this.padding = EdgeInsets.zero,
this.minSize,
this.borderRadius = const BorderRadius.all(Radius.circular(32)),
this.type,
});

const AppCupertinoButton.tinted({
required this.child,
required this.onPressed,
required this.color,
super.key,
this.disabledColor,
this.icon,
this.borderRadius = const BorderRadius.all(Radius.circular(12)),
this.padding = const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
this.type = CupertinoButtonType.tinted,
this.minSize,
});

final Widget child;
final Widget? icon;
final VoidCallback? onPressed;
final EdgeInsetsGeometry? padding;
final double? minSize;
final Color? color;
final Color? disabledColor;
final BorderRadius borderRadius;
final CupertinoButtonType? type;

@override
Widget build(BuildContext context) {
return CupertinoButton(
disabledColor: disabledColor ?? CupertinoColors.quaternarySystemFill,
onPressed: onPressed,
padding: padding ?? EdgeInsets.zero,
minSize: minSize ?? 0,
borderRadius: BorderRadius.circular(32),
color: color,
child: child,
final typeSize = type == null ? 0.0 : kMinInteractiveDimensionCupertino;
return Theme(
data: Theme.of(context).copyWith(
cupertinoOverrideTheme: CupertinoThemeData(
primaryColor: color ?? context.colorScheme.primary,
primaryContrastingColor: color ?? context.colorScheme.primary,
scaffoldBackgroundColor: color ?? context.colorScheme.primary,
textTheme: const CupertinoTextThemeData(
primaryColor: Colors.white,
textStyle: TextStyle(),
),
),
),
child: Builder(
builder: (context) {
return CupertinoButton(
disabledColor:
disabledColor ?? CupertinoColors.quaternarySystemFill,
onPressed: onPressed,
padding: padding,
minSize: minSize ?? typeSize,
borderRadius: borderRadius,
color: _calculateColor(context),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
if (icon != null)
Padding(
padding: const EdgeInsets.only(right: 4),
child: icon,
),
child,
],
),
);
},
),
);
}

Color? _calculateColor(BuildContext context) {
return switch (type) {
CupertinoButtonType.plain || null => null,
CupertinoButtonType.gray => CupertinoColors.systemFill,
CupertinoButtonType.tinted => color!.withOpacity(0.2),
CupertinoButtonType.filled => context.colorScheme.primary.darken()
};
}
}
8 changes: 4 additions & 4 deletions lib/common_widgets/app_cupertino_sliver_navigation_bar.dart
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import '../extensions/is_ios_or_macos_platform_extension.dart';

import '../extensions/is_ios_or_macos_platform_extension.dart';
import '../extensions/js_bottom_padding_extension.dart'
if (dart.library.js_interop) '../extensions/js_bottom_padding_extension_web.dart';

import '../extensions/media_query_context_extension.dart';
import '../extensions/theme_of_context_extension.dart';
import '../features/home/widgets/settings_modal_sheet.dart';
import '../utils/screen_size.dart';

class AppCupertinoSliverNavigationBar extends StatelessWidget {
const AppCupertinoSliverNavigationBar({
Expand All @@ -26,6 +25,7 @@ class AppCupertinoSliverNavigationBar extends StatelessWidget {

@override
Widget build(BuildContext context) {
final screenSize = context.screenSize;
return CupertinoSliverNavigationBar(
backgroundColor: Colors.transparent,
largeTitle: Text(
Expand All @@ -47,7 +47,7 @@ class AppCupertinoSliverNavigationBar extends StatelessWidget {
start: 16,
)
: const EdgeInsetsDirectional.symmetric(horizontal: 16),
trailing: context.querySize.isSmallScreen
trailing: screenSize.isSmall
? CupertinoButton(
padding: EdgeInsets.zero,
borderRadius: BorderRadius.zero,
Expand Down
51 changes: 51 additions & 0 deletions lib/common_widgets/app_loading_indicator.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// ignore_for_file: public_member_api_docs, sort_constructors_first
import 'package:flutter/material.dart';

class AppLoadingIndicator extends StatelessWidget {
const AppLoadingIndicator({
required this.showLoading,
this.sliver = false,
super.key,
});

final bool showLoading;
final bool sliver;
@override
Widget build(BuildContext context) {
if (sliver) {
return SliverToBoxAdapter(
child: AnimatedSize(
duration: const Duration(milliseconds: 300),
child: !showLoading
? const SizedBox.shrink()
: const Column(
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: EdgeInsets.all(16),
child: SizedBox(
height: 32,
width: 32,
child: CircularProgressIndicator.adaptive(),
),
),
],
),
),
);
}
return AnimatedSize(
duration: const Duration(milliseconds: 300),
child: !showLoading
? const SizedBox.shrink()
: const Padding(
padding: EdgeInsets.all(16),
child: SizedBox(
height: 32,
width: 32,
child: CircularProgressIndicator.adaptive(),
),
),
);
}
}
26 changes: 26 additions & 0 deletions lib/core/client_network_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,29 @@ enum Endpoint {
schools => '/schools',
};
}

class AppNetworkError extends Error {
AppNetworkError(this.message);

AppNetworkError.fromNetworkClientException(Object e)
: message = messageFromDio(e);

final String message;

@override
String toString() => message;

static String messageFromDio(Object e) {
if (e is! DioException) return 'Unknown error 🤷';
return switch (e.type) {
DioExceptionType.badCertificate => 'Bad certificate 📜',
DioExceptionType.connectionTimeout => 'Connection timeout ⏰',
DioExceptionType.sendTimeout => 'Send timeout ⏰',
DioExceptionType.receiveTimeout => 'Receive timeout ⏰',
DioExceptionType.badResponse => 'Bad response 🤷',
DioExceptionType.cancel => 'Request cancelled 🚫',
DioExceptionType.connectionError => 'Connection error 🚫',
DioExceptionType.unknown => 'No internet connection 🌎',
};
}
}
40 changes: 0 additions & 40 deletions lib/extensions/media_query_context_extension.dart

This file was deleted.

Loading

0 comments on commit 257a74f

Please sign in to comment.