diff --git a/core/.gitignore b/core/.gitignore new file mode 100644 index 000000000..657ee1109 --- /dev/null +++ b/core/.gitignore @@ -0,0 +1,124 @@ +# Miscellaneous +*.class +*.lock +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# Visual Studio Code related +.classpath +.project +.settings/ +.vscode/ + +# Flutter repo-specific +/bin/cache/ +/bin/mingit/ +/dev/benchmarks/mega_gallery/ +/dev/bots/.recipe_deps +/dev/bots/android_tools/ +/dev/devicelab/ABresults*.json +/dev/docs/doc/ +/dev/docs/flutter.docs.zip +/dev/docs/lib/ +/dev/docs/pubspec.yaml +/dev/integration_tests/**/xcuserdata +/dev/integration_tests/**/Pods +/packages/flutter/coverage/ +version +analysis_benchmark.json + +# packages file containing multi-root paths +.packages.generated + +# Flutter/Dart/Pub related +**/doc/api/ +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +**/generated_plugin_registrant.dart +.packages +.pub-cache/ +.pub/ +build/ +flutter_*.png +linked_*.ds +unlinked.ds +unlinked_spec.ds + +# Android related +**/android/.gradle +**/android/captures/ +**/android/local.properties +**/android/**/GeneratedPluginRegistrant.java +**/android/key.properties +*.jks + +# iOS/XCode related +**/ios/**/*.mode1v3 +**/ios/**/*.mode2v3 +**/ios/**/*.moved-aside +**/ios/**/*.pbxuser +**/ios/**/*.perspectivev3 +**/ios/**/*sync/ +**/ios/**/.sconsign.dblite +**/ios/**/.tags* +**/ios/**/.vagrant/ +**/ios/**/DerivedData/ +**/ios/**/Icon? +**/ios/**/Pods/ +**/ios/**/.symlinks/ +**/ios/**/profile +**/ios/**/xcuserdata +**/ios/.generated/ +**/ios/Flutter/.last_build_id +**/ios/Flutter/App.framework +**/ios/Flutter/Flutter.framework +**/ios/Flutter/Flutter.podspec +**/ios/Flutter/Generated.xcconfig +**/ios/Flutter/app.flx +**/ios/Flutter/app.zip +**/ios/Flutter/flutter_assets/ +**/ios/Flutter/flutter_export_environment.sh +**/ios/ServiceDefinitions.json +**/ios/Runner/GeneratedPluginRegistrant.* + +# macOS +**/macos/Flutter/GeneratedPluginRegistrant.swift +**/macos/Flutter/Flutter-Debug.xcconfig +**/macos/Flutter/Flutter-Release.xcconfig +**/macos/Flutter/Flutter-Profile.xcconfig + +# Coverage +coverage/ + +# Symbols +app.*.symbols + +# Exceptions to above rules. +!**/ios/**/default.mode1v3 +!**/ios/**/default.mode2v3 +!**/ios/**/default.pbxuser +!**/ios/**/default.perspectivev3 +!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages +!/dev/ci/**/Gemfile.lock + +#generated file +*.g.dart +messages_all.dart +messages_en.dart +messages_fr.dart +messages_messages.dart +messages_ru.dart +messages_vi.dart \ No newline at end of file diff --git a/core/.metadata b/core/.metadata new file mode 100644 index 000000000..be81bed0e --- /dev/null +++ b/core/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: d79295af24c3ed621c33713ecda14ad196fd9c31 + channel: stable + +project_type: module diff --git a/core/README.md b/core/README.md new file mode 100644 index 000000000..fb8ba0165 --- /dev/null +++ b/core/README.md @@ -0,0 +1,3 @@ +# core + +Core Module \ No newline at end of file diff --git a/core/lib/core.dart b/core/lib/core.dart new file mode 100644 index 000000000..5dd88b56f --- /dev/null +++ b/core/lib/core.dart @@ -0,0 +1,40 @@ +library core; + +// Extensions +export 'presentation/extensions/color_extension.dart'; +export 'presentation/extensions/url_extension.dart'; + +// Utils +export 'presentation/utils/theme_utils.dart'; +export 'presentation/utils/responsive_utils.dart'; +export 'presentation/utils/keyboard_utils.dart'; +export 'presentation/utils/style_utils.dart'; +export 'presentation/extensions/list_extensions.dart'; + +// Views +export 'presentation/views/text/slogan_builder.dart'; +export 'presentation/views/text/text_field_builder.dart'; +export 'presentation/views/text/input_decoration_builder.dart'; +export 'presentation/views/text/text_builder.dart'; +export 'presentation/views/responsive/responsive_widget.dart'; +export 'presentation/views/list/tree_view.dart'; + +// Resources +export 'presentation/resources/assets_paths.dart'; +export 'presentation/resources/image_paths.dart'; + +// Constants +export 'presentation/constants/constants_ui.dart'; +export 'data/constants/constant.dart'; + +// Network +export 'data/network/config/authorization_interceptors.dart'; +export 'data/network/config/dynamic_url_interceptors.dart'; +export 'data/network/config/endpoint.dart'; +export 'data/network/config/service_path.dart'; +export 'data/network/dio_client.dart'; + +// State +export 'presentation/state/success.dart'; +export 'presentation/state/failure.dart'; +export 'presentation/state/app_state.dart'; diff --git a/core/lib/data/constants/constant.dart b/core/lib/data/constants/constant.dart new file mode 100644 index 000000000..35f8cd9c5 --- /dev/null +++ b/core/lib/data/constants/constant.dart @@ -0,0 +1,7 @@ +class Constant { + static const userId = '_id'; + static const firstName = 'firstname'; + static const lastName = 'lastname'; + static const acceptHeaderDefault = 'application/json'; + static const contentTypeHeaderDefault = 'application/json'; +} \ No newline at end of file diff --git a/core/lib/data/network/config/authorization_interceptors.dart b/core/lib/data/network/config/authorization_interceptors.dart new file mode 100644 index 000000000..c6ea7b05c --- /dev/null +++ b/core/lib/data/network/config/authorization_interceptors.dart @@ -0,0 +1,19 @@ +import 'dart:convert'; + +import 'package:dio/dio.dart'; + +class AuthorizationInterceptors extends InterceptorsWrapper { + String? _authorization; + + void changeAuthorization(String? userName, String? password) { + _authorization = base64Encode(utf8.encode('$userName:$password')); + } + + @override + void onRequest(RequestOptions options, RequestInterceptorHandler handler) { + if (_authorization != null) { + options.headers['Authorization'] = 'Basic $_authorization'; + } + super.onRequest(options, handler); + } +} diff --git a/core/lib/data/network/config/dynamic_url_interceptors.dart b/core/lib/data/network/config/dynamic_url_interceptors.dart new file mode 100644 index 000000000..a8c0c366d --- /dev/null +++ b/core/lib/data/network/config/dynamic_url_interceptors.dart @@ -0,0 +1,17 @@ +import 'package:dio/dio.dart'; + +class DynamicUrlInterceptors extends InterceptorsWrapper { + String? _baseUrl; + + void changeBaseUrl(String? url) { + _baseUrl = url; + } + + @override + void onRequest(RequestOptions options, RequestInterceptorHandler handler) { + if (_baseUrl != null) { + options.baseUrl = _baseUrl!; + } + super.onRequest(options, handler); + } +} \ No newline at end of file diff --git a/core/lib/data/network/config/endpoint.dart b/core/lib/data/network/config/endpoint.dart new file mode 100644 index 000000000..b99292dae --- /dev/null +++ b/core/lib/data/network/config/endpoint.dart @@ -0,0 +1,5 @@ +import 'package:core/data/network/config/service_path.dart'; + +class Endpoint { + static final ServicePath authentication = ServicePath('/api/login'); +} diff --git a/core/lib/data/network/config/service_path.dart b/core/lib/data/network/config/service_path.dart new file mode 100644 index 000000000..db9fd6b0d --- /dev/null +++ b/core/lib/data/network/config/service_path.dart @@ -0,0 +1,19 @@ +class ServicePath { + final String path; + + ServicePath(this.path); +} + +extension ServicePathExtension on ServicePath { + String generateEndpointPath() { + return '$path'; + } + + String generateAuthenticationUrl(Uri? baseUrl) { + if (baseUrl == null) { + return generateEndpointPath(); + } else { + return baseUrl.origin + generateEndpointPath(); + } + } +} \ No newline at end of file diff --git a/core/lib/data/network/dio_client.dart b/core/lib/data/network/dio_client.dart new file mode 100644 index 000000000..9b36e4a47 --- /dev/null +++ b/core/lib/data/network/dio_client.dart @@ -0,0 +1,84 @@ +import 'package:dio/dio.dart'; + +class DioClient { + final Dio _dio; + + DioClient(this._dio); + + Map getHeaders() => Map.from(_dio.options.headers); + + Future get( + String path, { + Map? queryParameters, + Options? options, + CancelToken? cancelToken, + ProgressCallback? onReceiveProgress, + }) async { + return await _dio.get( + path, + queryParameters: queryParameters, + options: options, + cancelToken: cancelToken, + onReceiveProgress: onReceiveProgress) + .then((value) => value.data) + .catchError((error) => throw error); + } + + Future post( + String path, { + data, + Map? queryParameters, + Options? options, + CancelToken? cancelToken, + ProgressCallback? onSendProgress, + ProgressCallback? onReceiveProgress, + }) async { + return await _dio.post(path, + data: data, + queryParameters: queryParameters, + options: options, + cancelToken: cancelToken, + onSendProgress: onSendProgress, + onReceiveProgress: onReceiveProgress) + .then((value) => value.data) + .catchError((error) => throw error); + } + + Future delete( + String path, { + data, + Map? queryParameters, + Options? options, + CancelToken? cancelToken, + }) async { + return await _dio.delete( + path, + data: data, + queryParameters: queryParameters, + options: options, + cancelToken: cancelToken) + .then((value) => value.data) + .catchError((error) => throw(error)); + } + + Future put( + String path, { + data, + Map? queryParameters, + Options? options, + CancelToken? cancelToken, + ProgressCallback? onSendProgress, + ProgressCallback? onReceiveProgress + }) async { + return await _dio.put( + path, + data: data, + queryParameters: queryParameters, + options: options, + cancelToken: cancelToken, + onSendProgress: onSendProgress, + onReceiveProgress: onReceiveProgress) + .then((value) => value.data) + .catchError((error) => throw(error)); + } +} \ No newline at end of file diff --git a/core/lib/presentation/constants/constants_ui.dart b/core/lib/presentation/constants/constants_ui.dart new file mode 100644 index 000000000..c33906f1f --- /dev/null +++ b/core/lib/presentation/constants/constants_ui.dart @@ -0,0 +1,3 @@ +class ConstantsUI { + static const fontApp = 'CircularStd'; +} \ No newline at end of file diff --git a/core/lib/presentation/extensions/color_extension.dart b/core/lib/presentation/extensions/color_extension.dart new file mode 100644 index 000000000..fd0e06147 --- /dev/null +++ b/core/lib/presentation/extensions/color_extension.dart @@ -0,0 +1,36 @@ +import 'dart:ui' show Color; + +import 'package:flutter/material.dart'; + +extension AppColor on Color { + static const primaryColor = Color(0xFF837DFF); + static const primaryDarkColor = Color(0xFF1C1C1C); + static const primaryLightColor = Color(0xFFFFFFFF); + static const baseTextColor = Color(0xFF7E869B); + static const textFieldTextColor = Color(0xFF7E869B); + static const textFieldLabelColor = Color(0xFF7E869B); + static const textFieldHintColor = Color(0xFF757575); + static const textFieldBorderColor = Color(0xfff2f1fd); + static const textFieldFocusedBorderColor = Color(0xFF837DFF); + static const textFieldErrorBorderColor = Color(0xffFF5858); + static const buttonColor = Color(0xFF837DFF); + static const appColor = Color(0xFF3840F7); + static const nameUserColor = Color(0xFF182952); + static const emailUserColor = Color(0xFF7E869B); + static const userInformationBackgroundColor = Color(0xFFF5F5F7); + static const searchBorderColor = Color(0xFFEAEAEA); + static const searchHintTextColor = Color(0xFF7E869B); + static const mailboxSelectedBackgroundColor = Color(0xFFE6E5FF); + static const mailboxBackgroundColor = Color(0xFFFFFFFF); + static const mailboxSelectedTextColor = Color(0xFF3840F7); + static const mailboxTextColor = Color(0xFF182952); + static const mailboxSelectedTextNumberColor = Color(0xFF182952); + static const mailboxTextNumberColor = Color(0xFF837DFF); + static const mailboxSelectedIconColor = Color(0xFF3840F7); + static const mailboxIconColor = Color(0xFF7E869B); + static const storageBackgroundColor = Color(0xFFF5F5F7); + static const storageTitleColor = Color(0xFF7E869B); + static const storageMaxSizeColor = Color(0xFF101D43); + static const storageUseSizeColor = Color(0xFF2D0CFF); + static const myFolderTitleColor = Color(0xFF7E869B); +} diff --git a/core/lib/presentation/extensions/list_extensions.dart b/core/lib/presentation/extensions/list_extensions.dart new file mode 100644 index 000000000..7c3b568c6 --- /dev/null +++ b/core/lib/presentation/extensions/list_extensions.dart @@ -0,0 +1,19 @@ +import 'package:built_collection/built_collection.dart'; +import 'package:dartz/dartz.dart'; + +extension ListExtensions on List { + Tuple2, List> split(bool test(T element)) { + final validBuilder = ListBuilder(); + final invalidBuilder = ListBuilder(); + forEach((element) { + if (test(element)) { + validBuilder.add(element); + } else { + invalidBuilder.add(element); + } + }); + return Tuple2( + validBuilder.build().toList(), + validBuilder.build().toList()); + } +} \ No newline at end of file diff --git a/core/lib/presentation/extensions/url_extension.dart b/core/lib/presentation/extensions/url_extension.dart new file mode 100644 index 000000000..bb54a5ab1 --- /dev/null +++ b/core/lib/presentation/extensions/url_extension.dart @@ -0,0 +1,16 @@ +import 'package:flutter/foundation.dart'; + +extension URLExtension on String { + static final String prefixUrlHttps = 'https://'; + static final String prefixUrlHttp = 'http://'; + + String formatURLValid() { + if (startsWith(prefixUrlHttps)) { + return this; + } else if (startsWith(prefixUrlHttp)) { + return kReleaseMode ? replaceAll(prefixUrlHttp, prefixUrlHttps) : this; + } else { + return '$prefixUrlHttps${this}'; + } + } +} \ No newline at end of file diff --git a/core/lib/presentation/resources/assets_paths.dart b/core/lib/presentation/resources/assets_paths.dart new file mode 100644 index 000000000..f74f83107 --- /dev/null +++ b/core/lib/presentation/resources/assets_paths.dart @@ -0,0 +1,3 @@ +class AssetsPaths { + static const images = 'assets/images/'; +} \ No newline at end of file diff --git a/core/lib/presentation/resources/image_paths.dart b/core/lib/presentation/resources/image_paths.dart new file mode 100644 index 000000000..74f4f9649 --- /dev/null +++ b/core/lib/presentation/resources/image_paths.dart @@ -0,0 +1,23 @@ +import 'package:core/presentation/resources/assets_paths.dart'; + +class ImagePaths { + String get icTMailLogo => _getImagePath('tmail_logo.svg'); + String get icCloseMailbox => _getImagePath('ic_close.svg'); + String get icSearch => _getImagePath('ic_search.svg'); + String get icNextArrow => _getImagePath('ic_next_arrow.svg'); + String get icMailboxInbox => _getImagePath('ic_mailbox_inbox.svg'); + String get icMailboxTrash => _getImagePath('ic_mailbox_trash.svg'); + String get icMailboxAllMail => _getImagePath('ic_mailbox_allmail.svg'); + String get icMailboxDrafts => _getImagePath('ic_mailbox_draft.svg'); + String get icMailboxSent => _getImagePath('ic_mailbox_sent.svg'); + String get icMailboxSpam => _getImagePath('ic_mailbox_spam.svg'); + String get icMailboxTemplate => _getImagePath('ic_mailbox_template.svg'); + String get icMailboxFolder => _getImagePath('ic_mailbox_folder.svg'); + String get icMailboxNewFolder => _getImagePath('ic_mailbox_new_folder.svg'); + String get icFolderArrow => _getImagePath('ic_folder_arrow.svg'); + String get icExpandFolder => _getImagePath('ic_expand_folder.svg'); + + String _getImagePath(String imageName) { + return AssetsPaths.images + imageName; + } +} \ No newline at end of file diff --git a/core/lib/presentation/state/app_state.dart b/core/lib/presentation/state/app_state.dart new file mode 100644 index 000000000..37f9b1006 --- /dev/null +++ b/core/lib/presentation/state/app_state.dart @@ -0,0 +1,14 @@ +import 'package:core/core.dart'; +import 'package:dartz/dartz.dart'; +import 'package:meta/meta.dart'; +import 'package:equatable/equatable.dart'; + +@immutable +abstract class AppState with EquatableMixin { + final Either viewState; + + AppState(this.viewState); + + @override + List get props => [viewState]; +} \ No newline at end of file diff --git a/core/lib/presentation/state/failure.dart b/core/lib/presentation/state/failure.dart new file mode 100644 index 000000000..fc0118a58 --- /dev/null +++ b/core/lib/presentation/state/failure.dart @@ -0,0 +1,5 @@ +import 'package:equatable/equatable.dart'; + +abstract class Failure extends Equatable {} + +abstract class FeatureFailure extends Failure {} diff --git a/core/lib/presentation/state/success.dart b/core/lib/presentation/state/success.dart new file mode 100644 index 000000000..36f54c3ee --- /dev/null +++ b/core/lib/presentation/state/success.dart @@ -0,0 +1,15 @@ +import 'package:equatable/equatable.dart'; + +abstract class Success with EquatableMixin {} + +abstract class ViewState extends Success {} + +class UIState extends ViewState { + static final idle = UIState(); + static final loading = UIState(); + + UIState() : super(); + + @override + List get props => []; +} diff --git a/core/lib/presentation/utils/keyboard_utils.dart b/core/lib/presentation/utils/keyboard_utils.dart new file mode 100644 index 000000000..a1f306bb9 --- /dev/null +++ b/core/lib/presentation/utils/keyboard_utils.dart @@ -0,0 +1,10 @@ +import 'package:flutter/cupertino.dart'; + +class KeyboardUtils { + void hideKeyboard(BuildContext context) { + FocusScopeNode currentFocus = FocusScope.of(context); + if (!currentFocus.hasPrimaryFocus) { + currentFocus.unfocus(); + } + } +} \ No newline at end of file diff --git a/core/lib/presentation/utils/responsive_utils.dart b/core/lib/presentation/utils/responsive_utils.dart new file mode 100644 index 000000000..dfa7a2a4e --- /dev/null +++ b/core/lib/presentation/utils/responsive_utils.dart @@ -0,0 +1,37 @@ +import 'package:flutter/widgets.dart'; + +class ResponsiveUtils { + + static const int _minLargeWidth = 950; + static const int _minMediumWidth = 600; + + static const double _loginTextFieldWidthSmallScreen = 280.0; + static const double _loginTextFieldWidthLargeScreen = 320.0; + static const double _loginButtonWidth = 240.0; + + double getSizeWidthScreen(BuildContext context) { + return MediaQuery.of(context).size.width; + } + + double getSizeHeightScreen(BuildContext context) { + return MediaQuery.of(context).size.height; + } + + bool isLargeScreen(BuildContext context) { + return getSizeWidthScreen(context) >= _minLargeWidth; + } + + bool isSmallScreen(BuildContext context) { + return getSizeWidthScreen(context) < _minMediumWidth; + } + + bool isMediumScreen(BuildContext context) { + return getSizeWidthScreen(context) >= _minMediumWidth && getSizeWidthScreen(context) < _minLargeWidth; + } + + double getWidthLoginTextField(BuildContext context) => isSmallScreen(context) + ? _loginTextFieldWidthSmallScreen + : _loginTextFieldWidthLargeScreen; + + double getWidthLoginButton() => _loginButtonWidth; +} \ No newline at end of file diff --git a/core/lib/presentation/utils/style_utils.dart b/core/lib/presentation/utils/style_utils.dart new file mode 100644 index 000000000..847c3c0bf --- /dev/null +++ b/core/lib/presentation/utils/style_utils.dart @@ -0,0 +1,11 @@ +import 'package:core/presentation/extensions/color_extension.dart'; +import 'package:flutter/material.dart'; + +class CommonTextStyle { + static final textStyleNormal = TextStyle( + color: AppColor.primaryColor, + fontSize: 14, + fontStyle: FontStyle.normal, + fontWeight: FontWeight.normal, + ); +} \ No newline at end of file diff --git a/core/lib/presentation/utils/theme_utils.dart b/core/lib/presentation/utils/theme_utils.dart new file mode 100644 index 000000000..e3dd399cc --- /dev/null +++ b/core/lib/presentation/utils/theme_utils.dart @@ -0,0 +1,47 @@ +import 'package:core/presentation/constants/constants_ui.dart'; +import 'package:core/presentation/extensions/color_extension.dart'; +import 'package:flutter/material.dart'; + +ThemeData appTheme() { + return ThemeData( + scaffoldBackgroundColor: Colors.white, + fontFamily: ConstantsUI.fontApp, + appBarTheme: appBarTheme(), + textTheme: textTheme(), + visualDensity: VisualDensity.adaptivePlatformDensity, + ); +} + +InputDecorationTheme inputDecorationTheme() { + OutlineInputBorder outlineInputBorder = OutlineInputBorder( + borderRadius: BorderRadius.circular(28), + borderSide: BorderSide(color: AppColor.baseTextColor), + gapPadding: 10, + ); + return InputDecorationTheme( + floatingLabelBehavior: FloatingLabelBehavior.always, + contentPadding: EdgeInsets.symmetric(horizontal: 42, vertical: 20), + enabledBorder: outlineInputBorder, + focusedBorder: outlineInputBorder, + border: outlineInputBorder, + ); +} + +TextTheme textTheme() { + return TextTheme( + bodyText1: TextStyle(color: AppColor.baseTextColor), + bodyText2: TextStyle(color: AppColor.baseTextColor), + ); +} + +AppBarTheme appBarTheme() { + return AppBarTheme( + color: Colors.white, + elevation: 0, + brightness: Brightness.light, + iconTheme: IconThemeData(color: Colors.black), + textTheme: TextTheme( + headline6: TextStyle(color: Color(0XFF8B8B8B), fontSize: 18), + ), + ); +} \ No newline at end of file diff --git a/core/lib/presentation/views/list/tree_view.dart b/core/lib/presentation/views/list/tree_view.dart new file mode 100644 index 000000000..1c9c55c60 --- /dev/null +++ b/core/lib/presentation/views/list/tree_view.dart @@ -0,0 +1,133 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; + +class TreeView extends InheritedWidget { + final List children; + final bool startExpanded; + + TreeView({ + Key? key, + required List children, + bool startExpanded = false, + }) : this.children = children, this.startExpanded = startExpanded, super ( + key: key, + child: _TreeViewData( + children: children, + ), + ); + + static TreeView of(BuildContext context) { + return context.dependOnInheritedWidgetOfExactType()!; + } + + @override + bool updateShouldNotify(TreeView oldWidget) { + if (oldWidget.children == this.children && + oldWidget.startExpanded == this.startExpanded) { + return false; + } + return true; + } +} + +class _TreeViewData extends StatelessWidget { + final List children; + + const _TreeViewData({ + required this.children, + }); + + @override + Widget build(BuildContext context) { + return ListView.builder( + shrinkWrap: true, + primary: false, + itemCount: children.length, + itemBuilder: (context, index) { + return children.elementAt(index); + }, + ); + } +} + +class TreeViewChild extends StatefulWidget { + final bool? startExpanded; + final Widget parent; + final List children; + final VoidCallback? onTap; + + TreeViewChild({ + required this.parent, + required this.children, + this.startExpanded, + this.onTap, + Key? key, + }) : super(key: key); + + @override + TreeViewChildState createState() => TreeViewChildState(); + + TreeViewChild copyWith( + TreeViewChild source, { + bool? startExpanded, + Widget? parent, + List? children, + VoidCallback? onTap, + }) { + return TreeViewChild( + parent: parent ?? source.parent, + children: children ?? source.children, + startExpanded: startExpanded ?? source.startExpanded, + onTap: onTap ?? source.onTap, + ); + } +} + +class TreeViewChildState extends State { + bool? isExpanded; + + @override + void initState() { + super.initState(); + isExpanded = widget.startExpanded; + } + + @override + void didChangeDependencies() { + isExpanded = widget.startExpanded ?? TreeView.of(context).startExpanded; + super.didChangeDependencies(); + } + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + GestureDetector( + child: widget.parent, + onTap: () { + if (widget.onTap != null) { + widget.onTap!(); + } + toggleExpanded(); + }, + ), + AnimatedContainer( + duration: Duration(milliseconds: 400), + child: isExpanded! + ? Column( + mainAxisSize: MainAxisSize.min, + children: widget.children, + ) + : Offstage(), + ), + ], + ); + } + + void toggleExpanded() { + setState(() { + this.isExpanded = !this.isExpanded!; + }); + } +} \ No newline at end of file diff --git a/core/lib/presentation/views/responsive/responsive_widget.dart b/core/lib/presentation/views/responsive/responsive_widget.dart new file mode 100644 index 000000000..87a5dc86d --- /dev/null +++ b/core/lib/presentation/views/responsive/responsive_widget.dart @@ -0,0 +1,31 @@ +import 'package:core/presentation/utils/responsive_utils.dart'; +import 'package:flutter/material.dart'; + +class ResponsiveWidget extends StatelessWidget { + final Widget? largeScreen; + final Widget mediumScreen; + final Widget? smallScreen; + + final ResponsiveUtils responsiveUtils; + + const ResponsiveWidget({ + Key? key, + this.largeScreen, + required this.mediumScreen, + this.smallScreen, + required this.responsiveUtils, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return LayoutBuilder(builder: (context, constraints) { + if (responsiveUtils.isLargeScreen(context)) { + return largeScreen ?? mediumScreen; + } else if (responsiveUtils.isMediumScreen(context)) { + return mediumScreen; + } else { + return smallScreen ?? mediumScreen; + } + }); + } +} \ No newline at end of file diff --git a/core/lib/presentation/views/text/input_decoration_builder.dart b/core/lib/presentation/views/text/input_decoration_builder.dart new file mode 100644 index 000000000..50cd91ba5 --- /dev/null +++ b/core/lib/presentation/views/text/input_decoration_builder.dart @@ -0,0 +1,57 @@ +import 'package:flutter/material.dart'; + +abstract class InputDecorationBuilder { + String? prefixText; + String? labelText; + TextStyle? labelStyle; + String? hintText; + TextStyle? hintStyle; + EdgeInsets? contentPadding; + OutlineInputBorder? enabledBorder; + + InputDecorationBuilder setPrefixText(String newPrefixText) { + prefixText = newPrefixText; + return this; + } + + InputDecorationBuilder setLabelText(String newLabelText) { + labelText = newLabelText; + return this; + } + + InputDecorationBuilder setLabelStyle(TextStyle newLabelStyle) { + labelStyle = newLabelStyle; + return this; + } + + InputDecorationBuilder setHintText(String newHintText) { + hintText = newHintText; + return this; + } + + InputDecorationBuilder setHintStyle(TextStyle newHintStyle) { + hintStyle = newHintStyle; + return this; + } + + InputDecorationBuilder setContentPadding(EdgeInsets newContentPadding) { + contentPadding = newContentPadding; + return this; + } + + InputDecorationBuilder setEnabledBorder(OutlineInputBorder newEnabledBorder) { + enabledBorder = newEnabledBorder; + return this; + } + + InputDecoration build() { + return InputDecoration( + prefixText: prefixText, + labelText: labelText, + labelStyle: labelStyle, + hintText: hintText, + hintStyle: hintStyle, + contentPadding: contentPadding, + enabledBorder: enabledBorder); + } +} \ No newline at end of file diff --git a/core/lib/presentation/views/text/slogan_builder.dart b/core/lib/presentation/views/text/slogan_builder.dart new file mode 100644 index 000000000..6a3cb59ff --- /dev/null +++ b/core/lib/presentation/views/text/slogan_builder.dart @@ -0,0 +1,51 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +/// A builder which builds a reusable slogan widget. +/// This contains the logo and the slogan text. +/// The elements are arranged in a column. +class SloganBuilder { + Key? _key; + String? _text; + TextStyle? _textStyle; + TextAlign? _textAlign; + String? _logo; + + SloganBuilder key(Key key) { + _key = key; + return this; + } + + SloganBuilder setSloganText(String text) { + _text = text; + return this; + } + + SloganBuilder setSloganTextStyle(TextStyle textStyle) { + _textStyle = textStyle; + return this; + } + + SloganBuilder setSloganTextAlign(TextAlign textAlign) { + _textAlign = textAlign; + return this; + } + + SloganBuilder setLogo(String logo) { + _logo = logo; + return this; + } + + Widget build() { + return Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + _logo != null ? SvgPicture.asset(_logo!, width: 150, height: 150) : SizedBox.shrink(), + Padding( + padding: EdgeInsets.only(top: 16, left: 16, right: 16), + child: Text(_text ?? '', key: _key, style: _textStyle, textAlign: _textAlign), + ), + ], + ); + } +} diff --git a/core/lib/presentation/views/text/text_builder.dart b/core/lib/presentation/views/text/text_builder.dart new file mode 100644 index 000000000..63a42a510 --- /dev/null +++ b/core/lib/presentation/views/text/text_builder.dart @@ -0,0 +1,40 @@ +import 'package:core/presentation/utils/style_utils.dart'; +import 'package:flutter/material.dart'; + +class TextBuilder { + String? _text; + TextStyle? _textStyle; + TextAlign? _textAlign; + Key? _key; + + TextBuilder key(Key key) { + _key = key; + return this; + } + + TextBuilder text(String text) { + _text = text; + return this; + } + + TextBuilder textStyle(TextStyle textStyle) { + _textStyle = textStyle; + return this; + } + + TextBuilder textAlign(TextAlign textAlign) { + _textAlign = textAlign; + return this; + } + + Text build() { + return Text(_text ?? '', key: _key ?? Key('TextBuilder'), style: _textStyle ?? CommonTextStyle.textStyleNormal, textAlign: _textAlign ?? TextAlign.center); + } +} + +class CenterTextBuilder extends TextBuilder { + @override + Text build() { + return Text(_text ?? '', key: _key ?? Key('TextBuilder'), style: _textStyle, textAlign: TextAlign.center); + } +} \ No newline at end of file diff --git a/core/lib/presentation/views/text/text_field_builder.dart b/core/lib/presentation/views/text/text_field_builder.dart new file mode 100644 index 000000000..46f919ddb --- /dev/null +++ b/core/lib/presentation/views/text/text_field_builder.dart @@ -0,0 +1,53 @@ +import 'package:core/presentation/extensions/color_extension.dart'; +import 'package:flutter/material.dart'; + +class TextFieldBuilder { + Key? _key; + ValueChanged? _onTextChange; + TextStyle? _textStyle; + TextInputAction? _textInputAction; + InputDecoration? _inputDecoration; + bool? _obscureText; + + TextFieldBuilder key(Key key) { + _key = key; + return this; + } + + TextFieldBuilder onChange(ValueChanged onChange) { + _onTextChange = onChange; + return this; + } + + TextFieldBuilder textStyle(TextStyle style) { + _textStyle = style; + return this; + } + + TextFieldBuilder textInputAction(TextInputAction inputAction) { + _textInputAction = inputAction; + return this; + } + + TextFieldBuilder textDecoration(InputDecoration inputDecoration) { + _inputDecoration = inputDecoration; + return this; + } + + TextFieldBuilder obscureText(bool obscureText) { + _obscureText = obscureText; + return this; + } + + TextField build() { + return TextField( + key: _key ?? Key('TextFieldBuilder'), + onChanged: _onTextChange, + cursorColor: AppColor.primaryColor, + textInputAction: _textInputAction, + decoration: _inputDecoration, + style: _textStyle ?? TextStyle(color: AppColor.textFieldTextColor), + obscureText: _obscureText ?? false, + ); + } +} \ No newline at end of file diff --git a/core/pubspec.yaml b/core/pubspec.yaml new file mode 100644 index 000000000..d49e9aa35 --- /dev/null +++ b/core/pubspec.yaml @@ -0,0 +1,97 @@ +name: core +description: Core Module + +# The following defines the version and build number for your application. +# A version number is three numbers separated by dots, like 1.2.43 +# followed by an optional build number separated by a +. +# Both the version and the builder number may be overridden in flutter +# build by specifying --build-name and --build-number, respectively. +# In Android, build-name is used as versionName while build-number used as versionCode. +# Read more about Android versioning at https://developer.android.com/studio/publish/versioning +# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. +# Read more about iOS versioning at +# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html +# +# This version is used _only_ for the Runner app, which is used if you just do +# a `flutter run` or a `flutter make-host-app-editable`. It has no impact +# on any other native host app that you embed your Flutter project into. +version: 1.0.0+1 + +environment: + sdk: ">=2.12.0 <3.0.0" + +dependencies: + flutter: + sdk: flutter + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.2 + + # flutter_svg + flutter_svg: 0.22.0 + + # Http client + dio: 4.0.0 + + # dartz + dartz: 0.10.0-nullsafety.2 + + # equatable + equatable: 2.0.3 + + built_collection: 5.1.0 + +dev_dependencies: + flutter_test: + sdk: flutter + + build_runner: 2.0.5 +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +flutter: + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # To add Flutter specific assets to your application, add an assets section, + # like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware. + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/assets-and-images/#from-packages + + # To add Flutter specific custom fonts to your application, add a fonts + # section here, in this "flutter" section. Each entry in this list should + # have a "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/custom-fonts/#from-packages + + + # This section identifies your Flutter project as a module meant for + # embedding in a native host app. These identifiers should _not_ ordinarily + # be changed after generation - they are used to ensure that the tooling can + # maintain consistency when adding or modifying assets and plugins. + # They also do not have any bearing on your native host application's + # identifiers, which may be completely independent or the same as these. \ No newline at end of file