From fbfbe03f2e77ae6d5098e41339d78885c3ada761 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bedirhan=20Sa=C4=9Flam?= Date: Sat, 12 Nov 2022 21:04:05 +0300 Subject: [PATCH 1/3] #some changes --- README.md | 152 +++++++---- android/app/build.gradle | 2 +- .../some_global_bloc/some_global_bloc.dart | 15 - .../some_global_bloc/some_global_event.dart | 5 - .../some_global_bloc/some_global_state.dart | 10 - lib/core/base/bloc/app_bloc_observer.dart | 9 + lib/core/base/bloc/auth/auth_bloc.dart | 13 + lib/core/base/bloc/auth/auth_event.dart | 8 + lib/core/base/bloc/auth/auth_state.dart | 10 + lib/core/base/functions/base_functions.dart | 74 +++++ lib/core/base/state/base_state.dart | 10 + lib/core/base/view/base_view.dart | 42 +++ lib/core/bloc/app_bloc_observer.dart | 43 --- lib/core/constants.dart | 9 - lib/core/constants/app/app_constants.dart | 13 + lib/core/constants/enums/api_enums.dart | 1 + lib/core/dependency_injection.dart | 35 ++- lib/core/extensions/color_extensions.dart | 17 ++ lib/core/extensions/context_extensions.dart | 27 ++ lib/core/extensions/iterable_extensions.dart | 9 + lib/core/extensions/network_extensions.dart | 12 + lib/core/extensions/num_extensions.dart | 11 + lib/core/extensions/string_extensions.dart | 43 +++ .../local_storage/local_storage_manager.dart | 33 ++- lib/core/init/network/vexana_manager.dart | 19 ++ lib/core/init/routes/routes.dart | 19 ++ lib/core/{ => init}/theme/colors.dart | 0 lib/core/navigation.dart | 15 - lib/core/navigation.gr.dart | 43 --- lib/data/data_sources/some_service.dart | 0 lib/data/models/some_model.dart | 0 lib/data/repositories/some_repository.dart | 0 lib/main.dart | 92 +++---- lib/ui/screens/home/home.dart | 101 ------- lib/ui/widgets/some_custom_widget.dart | 0 .../home/bloc}/home_bloc.dart | 2 - .../home/bloc}/home_event.dart | 3 + .../home/bloc}/home_state.dart | 6 +- lib/view/home/home_view.dart | 14 + lib/view/home/service/home_service.dart | 10 + pubspec.lock | 258 +++++++++++++----- pubspec.yaml | 125 +++------ .../auth/auth_service_test.dart | 0 test/widget_test.dart | 3 +- 44 files changed, 777 insertions(+), 536 deletions(-) delete mode 100644 lib/bloc/some_global_bloc/some_global_bloc.dart delete mode 100644 lib/bloc/some_global_bloc/some_global_event.dart delete mode 100644 lib/bloc/some_global_bloc/some_global_state.dart create mode 100644 lib/core/base/bloc/app_bloc_observer.dart create mode 100644 lib/core/base/bloc/auth/auth_bloc.dart create mode 100644 lib/core/base/bloc/auth/auth_event.dart create mode 100644 lib/core/base/bloc/auth/auth_state.dart create mode 100644 lib/core/base/functions/base_functions.dart create mode 100644 lib/core/base/state/base_state.dart create mode 100644 lib/core/base/view/base_view.dart delete mode 100644 lib/core/bloc/app_bloc_observer.dart delete mode 100644 lib/core/constants.dart create mode 100644 lib/core/constants/app/app_constants.dart create mode 100644 lib/core/constants/enums/api_enums.dart create mode 100644 lib/core/extensions/color_extensions.dart create mode 100644 lib/core/extensions/context_extensions.dart create mode 100644 lib/core/extensions/iterable_extensions.dart create mode 100644 lib/core/extensions/network_extensions.dart create mode 100644 lib/core/extensions/num_extensions.dart create mode 100644 lib/core/extensions/string_extensions.dart rename lib/core/{ => init}/local_storage/local_storage_manager.dart (65%) create mode 100644 lib/core/init/network/vexana_manager.dart create mode 100644 lib/core/init/routes/routes.dart rename lib/core/{ => init}/theme/colors.dart (100%) delete mode 100644 lib/core/navigation.dart delete mode 100644 lib/core/navigation.gr.dart delete mode 100644 lib/data/data_sources/some_service.dart delete mode 100644 lib/data/models/some_model.dart delete mode 100644 lib/data/repositories/some_repository.dart delete mode 100644 lib/ui/screens/home/home.dart delete mode 100644 lib/ui/widgets/some_custom_widget.dart rename lib/{ui/screens/home/view_bloc => view/home/bloc}/home_bloc.dart (93%) rename lib/{ui/screens/home/view_bloc => view/home/bloc}/home_event.dart (67%) rename lib/{ui/screens/home/view_bloc => view/home/bloc}/home_state.dart (76%) create mode 100644 lib/view/home/home_view.dart create mode 100644 lib/view/home/service/home_service.dart rename lib/core/network/dio_manager.dart => test/auth/auth_service_test.dart (100%) diff --git a/README.md b/README.md index 59baca7..3cd1caa 100644 --- a/README.md +++ b/README.md @@ -3,13 +3,13 @@ This is a battery included [Flutter](https://flutter.dev/) project template. To the default setup have been added: - [flutter_bloc](https://bloclibrary.dev/) - Predictable and Highly testable state management library for Dart. -- [autoroute](https://pub.dev/packages/autoroute) - Robust and customizable Flutter navigation package. -- [dio](https://pub.dev/packages/dio) - Popular and powerful Http client for Dart. +- [go_router](https://pub.dev/packages/go_router) - Robust and customizable Flutter navigation package. +- [vexana](https://pub.dev/packages/vexana) - Popular and powerful Http client for Dart. - [json_serializable](https://pub.dev/packages/json_serializable) - Generates to/from json serialization code for our data models. - [easy_localization](https://pub.dev/packages/easy_localization) - Easy and Fast internationalization. - [bot_toast](https://pub.dev/packages/bot_toast) - Feature-Rich Toast/alert/notification/popup library for Flutter. - [flutter_secure_storage](https://pub.dev/packages/flutter_secure_storage) - A Flutter plugin to store data in secure storage (encrypted, not in plain text like shared_preferences). Eg. for storing user credentials, tokens... -- [flutter_screenutil](https://pub.dev/packages/flutter_screenutil) - A package for adapting screen and font size. +- [sizer](https://pub.dev/packages/sizer) - A package for adapting screen and font size. - [flutter_svg](https://pub.dev/packages/flutter_svg) - For rendering SVG assets. - Some tooling for project initial setup. - [flutter_native_splash](https://pub.dev/packages/flutter_native_splash) - Generates iOS, Android, and Web-native code for customizing the native splash screen background color and splash image. @@ -28,47 +28,54 @@ Initialize the Flutter project, add all the necessary dependencies mentioned abo dependencies: flutter: sdk: flutter - # ... - # ... - # Http client - dio: ^4.0.6 - # I18n + cupertino_icons: ^1.0.5 + + #Responsive + sizer: ^2.0.15 + + #Image + flutter_svg: ^1.1.6 + + #State Management + provider: ^6.0.4 + flutter_bloc: ^8.1.1 + + #JsonAnnotation + json_annotation: ^4.7.0 + equatable: ^2.0.5 + + #Network + vexana: ^3.0.1 + + #Locale Storage + flutter_secure_storage: ^6.0.0 + + #Navigation + go_router: ^5.1.1 + + #Localization intl: ^0.17.0 easy_localization: ^3.0.1 - # Secure String key-value storage - flutter_secure_storage: ^5.0.2 - # Dependency Injection - provider: ^6.0.3 - # State management - flutter_bloc: ^8.0.1 - equatable: ^2.0.3 - bloc: ^8.0.3 - # Navigation - auto_route: ^4.0.1 - # Global in-app alert/notification/toast/snackBar - bot_toast: ^4.0.2 - # Responsiveness - flutter_screenutil: ^5.5.3+2 - #SVG Support - flutter_svg: ^1.1.0 - # Data Model Json serialization annotations - json_annotation: ^4.5.0 - #Native Splash screen autoconfiguration - flutter_native_splash: ^2.2.3+1 + bot_toast: ^4.0.3 + #Native Splash Screen + flutter_native_splash: ^2.2.13 dev_dependencies: flutter_test: sdk: flutter - # ... - # ... + flutter_lints: ^2.0.1 + # Code generation - build_runner: ^2.1.11 - auto_route_generator: ^4.0.0 + build_runner: ^2.2.0 + auto_route_generator: ^5.0.3 + # Json serialization code generation - json_serializable: ^6.2.0 + json_serializable: ^6.3.1 + # Native Launcher Icons generator - flutter_launcher_icons: ^0.9.3 + flutter_launcher_icons: ^0.10.0 + # Change app package name/bundle id and app name change_app_package_name: ^1.1.0 rename: ^2.0.1 @@ -191,33 +198,60 @@ Main folders structure ...📄 {locale}.json 📂 lib - 📂 bloc - ...📂 bloc - 📄 {global_bloc}.dart - 📄 {global_bloc_event}.dart - 📄 {global_bloc_state}.dart - 📂 core + 📂 core + 📂 base 📂 bloc + 📂 auth + 📄 {auth_bloc}.dart + 📄 {auth_event}.dart + 📄 {auth_state}.dart 📄 app_bloc_observer.dart - 📂 local_storage - 📄 local_storage_manager.dart - 📂 network - 📄 dio_manager.dart - 📂 theme - 📄 colors.dart - 📄 constants.dart - 📄 dependency_injection.dart - 📄 navigation.dart - 📄 utils.dart - 📂 ui - 📂 widgets - ...📄 {common_widget}.dart - 📂 screens - ...📂 {screen}.dart - 📂 view_bloc - 📄 {screen_bloc}.dart - 📄 {screen_bloc_event}.dart - 📄 {screen_bloc_state}.dart + 📂 functions + 📄 base_functions.dart + 📂 model + 📂 service + 📂 state + 📂 view + 📂 components + 📂 button + 📂 text + 📂 textFormField + 📂 constants + 📂 app + 📄 app_constants.dart + 📂 enums + 📄 api_enums.dart + 📂 extensions + 📄 color_extensions.dart + 📄 context_extensions.dart + 📄 iterable_extensions.dart + 📄 network_extensions.dart + 📄 num_extensions.dart + 📄 string_extensions.dart + 📂 init + 📂 language + 📂 local_storage + 📄 local_storage_manager.dart + 📂 network + 📄 vexana_manager.dart + 📂 routes + 📄 routes.dart + 📂 theme + 📄 colors.dart + 📄 dependency_injection.dart + 📄 locale_keys.g.dart + 📂 view + 📂 auth + 📂 home + 📂 bloc + 📄 {home_bloc}.dart + 📄 {home_event}.dart + 📄 {home_state}.dart + 📂 model + 📂 service + 📄 vexana_manager.dart + 📂 widgets + 📄 home_view.dart 📄 main.dart 📂 test diff --git a/android/app/build.gradle b/android/app/build.gradle index 906780b..ff303f2 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -26,7 +26,7 @@ apply plugin: 'kotlin-android' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion flutter.compileSdkVersion + compileSdkVersion 33 ndkVersion flutter.ndkVersion compileOptions { diff --git a/lib/bloc/some_global_bloc/some_global_bloc.dart b/lib/bloc/some_global_bloc/some_global_bloc.dart deleted file mode 100644 index 904785c..0000000 --- a/lib/bloc/some_global_bloc/some_global_bloc.dart +++ /dev/null @@ -1,15 +0,0 @@ -import 'dart:async'; - -import 'package:bloc/bloc.dart'; -import 'package:equatable/equatable.dart'; - -part 'some_global_event.dart'; -part 'some_global_state.dart'; - -class SomeGlobalBloc extends Bloc { - SomeGlobalBloc() : super(SomeGlobalInitial()) { - on((event, emit) { - // TODO: implement event handler - }); - } -} diff --git a/lib/bloc/some_global_bloc/some_global_event.dart b/lib/bloc/some_global_bloc/some_global_event.dart deleted file mode 100644 index 792a49f..0000000 --- a/lib/bloc/some_global_bloc/some_global_event.dart +++ /dev/null @@ -1,5 +0,0 @@ -part of 'some_global_bloc.dart'; - -abstract class SomeGlobalEvent extends Equatable { - const SomeGlobalEvent(); -} diff --git a/lib/bloc/some_global_bloc/some_global_state.dart b/lib/bloc/some_global_bloc/some_global_state.dart deleted file mode 100644 index ec8970a..0000000 --- a/lib/bloc/some_global_bloc/some_global_state.dart +++ /dev/null @@ -1,10 +0,0 @@ -part of 'some_global_bloc.dart'; - -abstract class SomeGlobalState extends Equatable { - const SomeGlobalState(); -} - -class SomeGlobalInitial extends SomeGlobalState { - @override - List get props => []; -} diff --git a/lib/core/base/bloc/app_bloc_observer.dart b/lib/core/base/bloc/app_bloc_observer.dart new file mode 100644 index 0000000..e67dd33 --- /dev/null +++ b/lib/core/base/bloc/app_bloc_observer.dart @@ -0,0 +1,9 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; + +class AppBlocObserver extends BlocObserver { + @override + void onCreate(BlocBase bloc) { + // print("OnCreate Bloc: $bloc"); + super.onCreate(bloc); + } +} diff --git a/lib/core/base/bloc/auth/auth_bloc.dart b/lib/core/base/bloc/auth/auth_bloc.dart new file mode 100644 index 0000000..bcbec09 --- /dev/null +++ b/lib/core/base/bloc/auth/auth_bloc.dart @@ -0,0 +1,13 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; + +part 'auth_event.dart'; +part 'auth_state.dart'; + +class AuthBloc extends Bloc { + AuthBloc() : super(AuthInitial()) { + on((event, emit) { + // TODO: implement event handler + }); + } +} diff --git a/lib/core/base/bloc/auth/auth_event.dart b/lib/core/base/bloc/auth/auth_event.dart new file mode 100644 index 0000000..d86ac6b --- /dev/null +++ b/lib/core/base/bloc/auth/auth_event.dart @@ -0,0 +1,8 @@ +part of 'auth_bloc.dart'; + +abstract class AuthEvent extends Equatable { + const AuthEvent(); + + @override + List get props => []; +} diff --git a/lib/core/base/bloc/auth/auth_state.dart b/lib/core/base/bloc/auth/auth_state.dart new file mode 100644 index 0000000..b4ff226 --- /dev/null +++ b/lib/core/base/bloc/auth/auth_state.dart @@ -0,0 +1,10 @@ +part of 'auth_bloc.dart'; + +abstract class AuthState extends Equatable { + const AuthState(); + + @override + List get props => []; +} + +class AuthInitial extends AuthState {} diff --git a/lib/core/base/functions/base_functions.dart b/lib/core/base/functions/base_functions.dart new file mode 100644 index 0000000..0634e0f --- /dev/null +++ b/lib/core/base/functions/base_functions.dart @@ -0,0 +1,74 @@ +import 'dart:core'; +import 'dart:io'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; + +import '../../constants/app/app_constants.dart'; + +Widget platformIndicator() { + return Center( + child: Platform.isIOS + ? const CupertinoActivityIndicator() + : const CircularProgressIndicator(), + ); +} + +Widget platformBackButton({ + required VoidCallback onPressed, + AlignmentGeometry alignment = Alignment.topLeft, + Color? color = AppConstants.black, +}) { + return Align( + alignment: alignment, + child: Platform.isIOS + ? IconButton( + onPressed: onPressed, + icon: Icon( + Icons.arrow_back_ios, + color: color, + )) + : IconButton( + onPressed: onPressed, + icon: Icon( + Icons.arrow_back, + color: color, + )), + ); +} + +closePopup(BuildContext context) { + Navigator.of(context, rootNavigator: true).pop(); +} + +String toShortString(String value, {int countCharacter = 8}) { + return value.length > countCharacter + ? "${value.substring(0, countCharacter)}..." + : value; +} + +String toShortDoubleNumber(double value) { + String number = value.toString(); + return "${number.split(".").first}.${number.split(".")[1].substring(0, 2)}"; +} + +String convertStringDateYmm(String date) { + return DateFormat.yMMMEd('tr_TR').format(DateTime.parse(date)); +} + +String convertStringDateTime(String date) { + return DateFormat('dd/MM/yyyy hh:mm ').format(DateTime.parse(date)); +} + +String convertStringDateYmmm(String date) { + return DateFormat.yMMMMEEEEd('tr_TR').format(DateTime.parse(date)); +} + +String convertStringDateYMMd(String date) { + return DateFormat.yMMMd('tr_TR').format(DateTime.parse(date)); +} + +String convertStringDate4(String date) { + return DateFormat.Hm('tr_TR').format(DateTime.parse(date)); +} diff --git a/lib/core/base/state/base_state.dart b/lib/core/base/state/base_state.dart new file mode 100644 index 0000000..0397a63 --- /dev/null +++ b/lib/core/base/state/base_state.dart @@ -0,0 +1,10 @@ +import 'package:flutter/material.dart'; + +abstract class BaseState extends State { + ThemeData get themeData => Theme.of(context); + + double dynamicHeight(double value) => + MediaQuery.of(context).size.height * value; + double dyanmicWidth(double value) => + MediaQuery.of(context).size.width * value; +} diff --git a/lib/core/base/view/base_view.dart b/lib/core/base/view/base_view.dart new file mode 100644 index 0000000..17a18a2 --- /dev/null +++ b/lib/core/base/view/base_view.dart @@ -0,0 +1,42 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +class BaseView extends StatefulWidget { + final Widget Function(BuildContext context, T value) onPageBuilder; + final T viewModel; + final Function(T model) onModelReady; + final VoidCallback? onDispose; + + const BaseView( + {Key? key, + required this.viewModel, + required this.onPageBuilder, + required this.onModelReady, + this.onDispose}) + : super(key: key); + + @override + _BaseViewState createState() => _BaseViewState(); +} + +class _BaseViewState extends State> { + late T model; + + @override + void initState() { + model = widget.viewModel; + widget.onModelReady(model); + super.initState(); + } + + @override + void dispose() { + super.dispose(); + if (widget.onDispose != null) widget.onDispose!(); + } + + @override + Widget build(BuildContext context) { + return widget.onPageBuilder(context, model); + } +} diff --git a/lib/core/bloc/app_bloc_observer.dart b/lib/core/bloc/app_bloc_observer.dart deleted file mode 100644 index e8db4e7..0000000 --- a/lib/core/bloc/app_bloc_observer.dart +++ /dev/null @@ -1,43 +0,0 @@ -import 'package:bloc/bloc.dart'; -import 'package:flutter/foundation.dart'; - -class AppBlocObserver extends BlocObserver { - @override - void onCreate(BlocBase bloc) { - if (kDebugMode) { - print('OnCreate Bloc: ' + bloc.toString()); - } - super.onCreate(bloc); - } - - @override - void onClose(BlocBase bloc) { - super.onClose(bloc); - } - - @override - void onError(BlocBase bloc, Object error, StackTrace stackTrace) { - if (kDebugMode) { - print('Bloc Error: $error Bloc: $bloc'); - } - super.onError(bloc, error, stackTrace); - } - - @override - void onTransition(Bloc bloc, Transition transition) { - super.onTransition(bloc, transition); - } - - @override - void onChange(BlocBase bloc, Change change) { - super.onChange(bloc, change); - } - - @override - void onEvent(Bloc bloc, Object? event) { - if (kDebugMode) { - print('Bloc Event: $event Bloc: $bloc'); - } - super.onEvent(bloc, event); - } -} diff --git a/lib/core/constants.dart b/lib/core/constants.dart deleted file mode 100644 index ebfbd64..0000000 --- a/lib/core/constants.dart +++ /dev/null @@ -1,9 +0,0 @@ -import 'package:flutter/material.dart'; - - -//Locale -const translationsPath = 'assets/translations'; -const en = Locale('en'); -const supportedLocales = [ - en -]; diff --git a/lib/core/constants/app/app_constants.dart b/lib/core/constants/app/app_constants.dart new file mode 100644 index 0000000..51b4a46 --- /dev/null +++ b/lib/core/constants/app/app_constants.dart @@ -0,0 +1,13 @@ +import 'package:flutter/material.dart'; + +class AppConstants { + static AppConstants? _instance; + static AppConstants get instance { + _instance ??= AppConstants._init(); + return _instance!; + } + + AppConstants._init(); + + static const Color black = Color(0x0fffffff); +} diff --git a/lib/core/constants/enums/api_enums.dart b/lib/core/constants/enums/api_enums.dart new file mode 100644 index 0000000..9015869 --- /dev/null +++ b/lib/core/constants/enums/api_enums.dart @@ -0,0 +1 @@ +enum NetworkEnums { example } diff --git a/lib/core/dependency_injection.dart b/lib/core/dependency_injection.dart index 05af063..38e9e4a 100644 --- a/lib/core/dependency_injection.dart +++ b/lib/core/dependency_injection.dart @@ -1,4 +1,35 @@ +import 'package:flutter/cupertino.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_mobile_template/view/home/bloc/home_bloc.dart'; +import 'package:flutter_mobile_template/view/home/service/home_service.dart'; +import 'package:provider/provider.dart'; +import 'package:vexana/vexana.dart'; -final List repositoryProviders = []; -final List> globalBlocProviders = []; +import 'init/network/vexana_manager.dart'; + +class DependencyInjector { + static DependencyInjector? _instance; + + static DependencyInjector get instance { + _instance ??= DependencyInjector._init(); + return _instance!; + } + + late final INetworkManager networkManager; + late final IHomeService homeService; + late final HomeBloc homeBloc; + + DependencyInjector._init() { + networkManager = VexanaManager.instance.networkManager; + } + + List get repositoryProviders => [ + RepositoryProvider(create: (context) => homeService), + ]; + + List> get globalBlocProviders => [ + BlocProvider(create: (context) => homeBloc), + ]; + + List> get otherProviders => []; +} diff --git a/lib/core/extensions/color_extensions.dart b/lib/core/extensions/color_extensions.dart new file mode 100644 index 0000000..bb663d9 --- /dev/null +++ b/lib/core/extensions/color_extensions.dart @@ -0,0 +1,17 @@ +import 'dart:ui'; + +extension HexColor on Color { + static Color fromHex(String hexString) { + final buffer = StringBuffer(); + if (hexString.length == 6 || hexString.length == 7) buffer.write('ff'); + buffer.write(hexString.replaceFirst('#', '')); + return Color(int.parse(buffer.toString(), radix: 16)); + } + + /// Prefixes a hash sign if [leadingHashSign] is set to `true` (default is `true`). + String toHex({bool leadingHashSign = true}) => '${leadingHashSign ? '#' : ''}' + '${alpha.toRadixString(16).padLeft(2, '0')}' + '${red.toRadixString(16).padLeft(2, '0')}' + '${green.toRadixString(16).padLeft(2, '0')}' + '${blue.toRadixString(16).padLeft(2, '0')}'; +} diff --git a/lib/core/extensions/context_extensions.dart b/lib/core/extensions/context_extensions.dart new file mode 100644 index 0000000..7ea841d --- /dev/null +++ b/lib/core/extensions/context_extensions.dart @@ -0,0 +1,27 @@ +import 'package:flutter/material.dart'; + +extension ContextExtension on BuildContext { + MediaQueryData get mediaQuery => MediaQuery.of(this); + + TextTheme get textTheme => Theme.of(this).textTheme; + TextTheme get primaryTextTheme => Theme.of(this).primaryTextTheme; + + ColorScheme get colorScheme => Theme.of(this).colorScheme; + + ThemeData get appTheme => Theme.of(this); + + double get textScaleFactor => MediaQuery.of(this).textScaleFactor; +} + +extension MediaQueryExtension on BuildContext { + double get height => mediaQuery.size.height; + double get width => mediaQuery.size.width; + + double get lowValue => height * 0.01; + double get normalValue => height * 0.02; + double get mediumValue => height * 0.04; + double get highValue => height * 0.1; + + double dynamicWidth(double val) => width * val; + double dynamicHeight(double val) => height * val; +} diff --git a/lib/core/extensions/iterable_extensions.dart b/lib/core/extensions/iterable_extensions.dart new file mode 100644 index 0000000..c6440da --- /dev/null +++ b/lib/core/extensions/iterable_extensions.dart @@ -0,0 +1,9 @@ +extension IterableExtension on Iterable { + T? firstWhereIndexedOrNull(bool Function(int index, T element) test) { + var index = 0; + for (var element in this) { + if (test(index++, element)) return element; + } + return null; + } +} diff --git a/lib/core/extensions/network_extensions.dart b/lib/core/extensions/network_extensions.dart new file mode 100644 index 0000000..d846a13 --- /dev/null +++ b/lib/core/extensions/network_extensions.dart @@ -0,0 +1,12 @@ +import '../constants/enums/api_enums.dart'; + +extension NetworkExtensions on NetworkEnums { + String get key { + switch (this) { + case NetworkEnums.example: + return "${NetworkBaseEnums.template.name}/zalisoft"; + } + } +} + +enum NetworkBaseEnums { template } diff --git a/lib/core/extensions/num_extensions.dart b/lib/core/extensions/num_extensions.dart new file mode 100644 index 0000000..590c8f6 --- /dev/null +++ b/lib/core/extensions/num_extensions.dart @@ -0,0 +1,11 @@ +import 'package:flutter/cupertino.dart'; + +extension EmptyPadding on num { + SizedBox get ph => SizedBox(height: toDouble()); + SizedBox get pw => SizedBox(width: toDouble()); +} + +extension DoubleExtension on double { + String get toShortDoubleNumber => + "${toString().split(".").first}.${toString().split(".")[1].substring(0, 2)}"; +} diff --git a/lib/core/extensions/string_extensions.dart b/lib/core/extensions/string_extensions.dart new file mode 100644 index 0000000..a7f4548 --- /dev/null +++ b/lib/core/extensions/string_extensions.dart @@ -0,0 +1,43 @@ +extension ImagePath on String { + String get toSvg => "assets/icons/svg_$this.svg"; + + String get toPng => "assets/images/ig_$this.png"; + + String get toJpeg => "assets/images/ig_$this.jpg"; + + String get toPngIc => "assets/icons/ic_$this.png"; + + String get toJpegIc => "assets/icons/ic_$this.jpg"; +} + +extension StringValidatorExtension on String? { + bool get isNullOrEmpty => this == null || this!.isEmpty; + bool get isNotNullOrNoEmpty => this != null && this!.isNotEmpty; +} + +extension FormValidate on String { + String? get emailValidate => + contains("@") ? null : "Uygun bir e-mail adresi giriniz !"; +} + +extension StringExtensions on String { + String getFirstWord() { + List wordList = split(""); + + if (wordList.isNotEmpty) { + return wordList[0]; + } else { + return ' '; + } + } +} + +extension NumberParsing on String { + int get parseInt => int.parse(this); + + int? get tryParseInt => int.tryParse(this); + + double get parseDouble => double.parse(this); + + double? get tryParseDouble => double.tryParse(this); +} diff --git a/lib/core/local_storage/local_storage_manager.dart b/lib/core/init/local_storage/local_storage_manager.dart similarity index 65% rename from lib/core/local_storage/local_storage_manager.dart rename to lib/core/init/local_storage/local_storage_manager.dart index 0e0aceb..f755dc5 100644 --- a/lib/core/local_storage/local_storage_manager.dart +++ b/lib/core/init/local_storage/local_storage_manager.dart @@ -3,16 +3,21 @@ import 'dart:convert'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; class LocalStorageManager { - LocalStorageManager._(); - static const _iosOptions = IOSOptions(accessibility: IOSAccessibility.first_unlock); + static const _androidOptions = + AndroidOptions(encryptedSharedPreferences: true); + static const _iosOptions = + IOSOptions(accessibility: KeychainAccessibility.first_unlock); - static const FlutterSecureStorage _secureStorage = FlutterSecureStorage(iOptions: _iosOptions); + static const FlutterSecureStorage _secureStorage = + FlutterSecureStorage(iOptions: _iosOptions, aOptions: _androidOptions); - static Future?> getKeys() async => (await _secureStorage.readAll()).keys.toSet(); + static Future?> getKeys() async => + (await _secureStorage.readAll()).keys.toSet(); - static Future containsKey(String key) async => await _secureStorage.containsKey(key: key); + static Future containsKey(String key) async => + await _secureStorage.containsKey(key: key); static Future setString(String key, String value) async => await _secureStorage.write(key: key, value: value); @@ -28,13 +33,17 @@ class LocalStorageManager { await _secureStorage.write(key: key, value: buffer); } - static Future setStringList(String key, List value) => _setList(key, value); + static Future setStringList(String key, List value) => + _setList(key, value); - static Future setNumList(String key, List value) => _setList(key, value); + static Future setNumList(String key, List value) => + _setList(key, value); - static Future setBoolList(String key, List value) => _setList(key, value); + static Future setBoolList(String key, List value) => + _setList(key, value); - static Future getString(String key) async => await _secureStorage.read(key: key); + static Future getString(String key) async => + await _secureStorage.read(key: key); static Future getBool(String key) async { String? string = await _secureStorage.read(key: key); @@ -58,13 +67,15 @@ class LocalStorageManager { return list; } - static Future?> getStringList(String key) => _getList(key); + static Future?> getStringList(String key) => + _getList(key); static Future?> getNumList(String key) => _getList(key); static Future?> getBoolList(String key) => _getList(key); - static Future remove(String key) async => await _secureStorage.delete(key: key); + static Future remove(String key) async => + await _secureStorage.delete(key: key); static Future clearAll() async => await _secureStorage.deleteAll(); } diff --git a/lib/core/init/network/vexana_manager.dart b/lib/core/init/network/vexana_manager.dart new file mode 100644 index 0000000..84db07b --- /dev/null +++ b/lib/core/init/network/vexana_manager.dart @@ -0,0 +1,19 @@ +import 'package:vexana/vexana.dart'; + +class VexanaManager { + static VexanaManager? _instance; + static VexanaManager get instance { + if (_instance != null) return _instance!; + _instance = VexanaManager._init(); + return _instance!; + } + + static const String baseUrl = "http://localhost:3000/"; + + VexanaManager._init(); + + INetworkManager networkManager = NetworkManager( + isEnableLogger: true, + options: BaseOptions(baseUrl: baseUrl), + ); +} diff --git a/lib/core/init/routes/routes.dart b/lib/core/init/routes/routes.dart new file mode 100644 index 0000000..ba3b5c7 --- /dev/null +++ b/lib/core/init/routes/routes.dart @@ -0,0 +1,19 @@ +import 'package:flutter/cupertino.dart'; +import 'package:go_router/go_router.dart'; + +import '../../../view/home/home_view.dart'; + +final GlobalKey mainNavigatorKey = + GlobalKey(debugLabel: 'main'); + +final routes = GoRouter( + initialLocation: '/home', + routes: [ + GoRoute( + path: '/home', + builder: (context, state) { + return const HomeView(); + }, + ), + ], +); diff --git a/lib/core/theme/colors.dart b/lib/core/init/theme/colors.dart similarity index 100% rename from lib/core/theme/colors.dart rename to lib/core/init/theme/colors.dart diff --git a/lib/core/navigation.dart b/lib/core/navigation.dart deleted file mode 100644 index 679516a..0000000 --- a/lib/core/navigation.dart +++ /dev/null @@ -1,15 +0,0 @@ -import 'package:auto_route/auto_route.dart'; -import '../ui/screens/home/home.dart'; - - -// @CupertinoAutoRouter -// @MaterialAutoRouter -// @CustomAutoRouter -@AdaptiveAutoRouter( - replaceInRouteName: 'Page,Route,Screen', - routes: [ - AutoRoute(path: '/', page: HomeScreen, initial: true), - RedirectRoute(path: '*', redirectTo: '/main') - ], -) -class $AppRouter {} diff --git a/lib/core/navigation.gr.dart b/lib/core/navigation.gr.dart deleted file mode 100644 index 785dd70..0000000 --- a/lib/core/navigation.gr.dart +++ /dev/null @@ -1,43 +0,0 @@ -// ************************************************************************** -// AutoRouteGenerator -// ************************************************************************** - -// GENERATED CODE - DO NOT MODIFY BY HAND - -// ************************************************************************** -// AutoRouteGenerator -// ************************************************************************** -// -// ignore_for_file: type=lint - -import 'package:auto_route/auto_route.dart' as _i2; -import 'package:flutter/material.dart' as _i3; -import 'package:flutter_mobile_template/ui/screens/home/home.dart' as _i1; - -class AppRouter extends _i2.RootStackRouter { - AppRouter([_i3.GlobalKey<_i3.NavigatorState>? navigatorKey]) - : super(navigatorKey); - - @override - final Map pagesMap = { - HomeRoute.name: (routeData) { - return _i2.AdaptivePage( - routeData: routeData, child: const _i1.HomeScreen()); - } - }; - - @override - List<_i2.RouteConfig> get routes => [ - _i2.RouteConfig(HomeRoute.name, path: '/'), - _i2.RouteConfig('*#redirect', - path: '*', redirectTo: '/main', fullMatch: true) - ]; -} - -/// generated route for -/// [_i1.HomeScreen] -class HomeRoute extends _i2.PageRouteInfo { - const HomeRoute() : super(HomeRoute.name, path: '/'); - - static const String name = 'HomeRoute'; -} diff --git a/lib/data/data_sources/some_service.dart b/lib/data/data_sources/some_service.dart deleted file mode 100644 index e69de29..0000000 diff --git a/lib/data/models/some_model.dart b/lib/data/models/some_model.dart deleted file mode 100644 index e69de29..0000000 diff --git a/lib/data/repositories/some_repository.dart b/lib/data/repositories/some_repository.dart deleted file mode 100644 index e69de29..0000000 diff --git a/lib/main.dart b/lib/main.dart index 12db9c9..824bf73 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,67 +1,57 @@ -import 'package:bloc/bloc.dart'; +import 'dart:async'; import 'package:bot_toast/bot_toast.dart'; -import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_mobile_template/core/theme/colors.dart'; -import 'package:flutter_native_splash/flutter_native_splash.dart'; -import 'package:flutter_screenutil/flutter_screenutil.dart'; - -import 'core/bloc/app_bloc_observer.dart'; -import 'core/constants.dart'; -import 'core/navigation.gr.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter_mobile_template/core/init/routes/routes.dart'; +import 'package:flutter_mobile_template/core/init/theme/colors.dart'; +import 'package:flutter_mobile_template/view/home/home_view.dart'; +import 'package:intl/date_symbol_data_local.dart'; +import 'package:provider/provider.dart'; +import 'package:sizer/sizer.dart'; +import 'core/base/bloc/app_bloc_observer.dart'; +import 'core/dependency_injection.dart'; -Future main() async { - final widgetsBinding = WidgetsFlutterBinding.ensureInitialized(); - FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding); +void main() async { + WidgetsFlutterBinding.ensureInitialized(); + initializeDateFormatting('tr'); await EasyLocalization.ensureInitialized(); - await ScreenUtil.ensureScreenSize(); - BlocOverrides.runZoned( - () { - // TODO: Dependency Injection - runApp( - EasyLocalization( - supportedLocales: supportedLocales, - path: translationsPath, - fallbackLocale: en, - useOnlyLangCode: true, - child: MyApp(), + + runZoned( + () => runApp( + MultiRepositoryProvider( + providers: DependencyInjector.instance.repositoryProviders, + child: MultiBlocProvider( + providers: DependencyInjector.instance.globalBlocProviders, + child: const MyApp(), ), - ); - }, - blocObserver: AppBlocObserver(), + ), + ), ); + Bloc.observer = AppBlocObserver(); } class MyApp extends StatelessWidget { - MyApp({Key? key}) : super(key: key); - - final _appRouter = AppRouter(); - - final botToastBuilder = BotToastInit(); + const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { - return MaterialApp.router( - title: 'Flutter Mobile App Template', - debugShowCheckedModeBanner: false, - localizationsDelegates: context.localizationDelegates, - supportedLocales: context.supportedLocales, - locale: context.locale, - routerDelegate: _appRouter.delegate( - navigatorObservers: () => [BotToastNavigatorObserver()], - ), - routeInformationParser: _appRouter.defaultRouteParser(), - theme: ThemeData( - primarySwatch: primarySwatch, - ), - builder: (context, child) { - ScreenUtil.init( - context, - // designSize: const Size(360, 690), - // minTextAdapt: true, - // splitScreenMode: true, + initializeDateFormatting('tr'); + return Sizer( + builder: (context, orientation, deviceType) { + final botToastBuilder = BotToastInit(); + return MaterialApp.router( + routerConfig: routes, + builder: (context, child) => botToastBuilder( + context, + const HomeView(), + ), + title: 'Flutter Mobile App Template', + debugShowCheckedModeBanner: false, + theme: ThemeData( + primarySwatch: primarySwatch, + ), ); - return botToastBuilder.call(context, child); }, ); } diff --git a/lib/ui/screens/home/home.dart b/lib/ui/screens/home/home.dart deleted file mode 100644 index 85254a8..0000000 --- a/lib/ui/screens/home/home.dart +++ /dev/null @@ -1,101 +0,0 @@ -import 'package:easy_localization/src/public_ext.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_native_splash/flutter_native_splash.dart'; - -import '../../../core/locale_keys.g.dart'; - -class HomeScreen extends StatefulWidget { - const HomeScreen({Key? key}) : super(key: key); - // This widget is the home page of your application. It is stateful, meaning - // that it has a State object (defined below) that contains fields that affect - // how it looks. - @override - State createState() => _HomeScreenState(); -} - -class _HomeScreenState extends State { - int _counter = 0; - - void _incrementCounter() { - setState(() { - // This call to setState tells the Flutter framework that something has - // changed in this State, which causes it to rerun the build method below - // so that the display can reflect the updated values. If we changed - // _counter without calling setState(), then the build method would not be - // called again, and so nothing would appear to happen. - _counter++; - }); - } - - @override - void initState() { - super.initState(); - initialization(); - } - - void initialization() async { - // This is where you can initialize the resources needed by your app while - // the splash screen is displayed. Remove the following example because - // delaying the user experience is a bad design practice! - // ignore_for_file: avoid_print - print('ready in 3...'); - await Future.delayed(const Duration(seconds: 1)); - print('ready in 2...'); - await Future.delayed(const Duration(seconds: 1)); - print('ready in 1...'); - await Future.delayed(const Duration(seconds: 1)); - print('go!'); - FlutterNativeSplash.remove(); - } - @override - Widget build(BuildContext context) { - // This method is rerun every time setState is called, for instance as done - // by the _incrementCounter method above. - // - // The Flutter framework has been optimized to make rerunning build methods - // fast, so that you can just rebuild anything that needs updating rather - // than having to individually change instances of widgets. - return Scaffold( - appBar: AppBar( - // Here we take the value from the MyHomePage object that was created by - // the App.build method, and use it to set our appbar title. - title: Text(LocaleKeys.appName.tr()), - ), - body: Center( - // Center is a layout widget. It takes a single child and positions it - // in the middle of the parent. - child: Column( - // Column is also a layout widget. It takes a list of children and - // arranges them vertically. By default, it sizes itself to fit its - // children horizontally, and tries to be as tall as its parent. - // - // Invoke "debug painting" (press "p" in the console, choose the - // "Toggle Debug Paint" action from the Flutter Inspector in Android - // Studio, or the "Toggle Debug Paint" command in Visual Studio Code) - // to see the wireframe for each widget. - // - // Column has various properties to control how it sizes itself and - // how it positions its children. Here we use mainAxisAlignment to - // center the children vertically; the main axis here is the vertical - // axis because Columns are vertical (the cross axis would be - // horizontal). - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text( - 'You have pushed the button this many times:', - ), - Text( - '$_counter', - style: Theme.of(context).textTheme.headline4, - ), - ], - ), - ), - floatingActionButton: FloatingActionButton( - onPressed: _incrementCounter, - tooltip: 'Increment', - child: const Icon(Icons.add), - ), // This trailing comma makes auto-formatting nicer for build methods. - ); - } -} diff --git a/lib/ui/widgets/some_custom_widget.dart b/lib/ui/widgets/some_custom_widget.dart deleted file mode 100644 index e69de29..0000000 diff --git a/lib/ui/screens/home/view_bloc/home_bloc.dart b/lib/view/home/bloc/home_bloc.dart similarity index 93% rename from lib/ui/screens/home/view_bloc/home_bloc.dart rename to lib/view/home/bloc/home_bloc.dart index 8a4f54c..ba65316 100644 --- a/lib/ui/screens/home/view_bloc/home_bloc.dart +++ b/lib/view/home/bloc/home_bloc.dart @@ -1,5 +1,3 @@ -import 'dart:async'; - import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; diff --git a/lib/ui/screens/home/view_bloc/home_event.dart b/lib/view/home/bloc/home_event.dart similarity index 67% rename from lib/ui/screens/home/view_bloc/home_event.dart rename to lib/view/home/bloc/home_event.dart index 7375db7..9d94475 100644 --- a/lib/ui/screens/home/view_bloc/home_event.dart +++ b/lib/view/home/bloc/home_event.dart @@ -2,4 +2,7 @@ part of 'home_bloc.dart'; abstract class HomeEvent extends Equatable { const HomeEvent(); + + @override + List get props => []; } diff --git a/lib/ui/screens/home/view_bloc/home_state.dart b/lib/view/home/bloc/home_state.dart similarity index 76% rename from lib/ui/screens/home/view_bloc/home_state.dart rename to lib/view/home/bloc/home_state.dart index 334d15d..d87458c 100644 --- a/lib/ui/screens/home/view_bloc/home_state.dart +++ b/lib/view/home/bloc/home_state.dart @@ -2,9 +2,9 @@ part of 'home_bloc.dart'; abstract class HomeState extends Equatable { const HomeState(); -} - -class HomeInitial extends HomeState { + @override List get props => []; } + +class HomeInitial extends HomeState {} diff --git a/lib/view/home/home_view.dart b/lib/view/home/home_view.dart new file mode 100644 index 0000000..221f8dd --- /dev/null +++ b/lib/view/home/home_view.dart @@ -0,0 +1,14 @@ +import 'package:flutter/material.dart'; + +class HomeView extends StatelessWidget { + const HomeView({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + body: SafeArea( + child: Container(), + ), + ); + } +} diff --git a/lib/view/home/service/home_service.dart b/lib/view/home/service/home_service.dart new file mode 100644 index 0000000..88588af --- /dev/null +++ b/lib/view/home/service/home_service.dart @@ -0,0 +1,10 @@ +import 'package:vexana/vexana.dart'; + +abstract class IHomeService { + final INetworkManager networkManager; + IHomeService(this.networkManager); +} + +class HomeService extends IHomeService { + HomeService(super.networkManager); +} diff --git a/pubspec.lock b/pubspec.lock index 795ce26..48c86bb 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -7,14 +7,14 @@ packages: name: _fe_analyzer_shared url: "https://pub.dartlang.org" source: hosted - version: "38.0.0" + version: "50.0.0" analyzer: dependency: transitive description: name: analyzer url: "https://pub.dartlang.org" source: hosted - version: "3.4.1" + version: "5.2.0" archive: dependency: transitive description: @@ -35,28 +35,28 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.8.2" + version: "2.9.0" auto_route: - dependency: "direct main" + dependency: transitive description: name: auto_route url: "https://pub.dartlang.org" source: hosted - version: "4.2.1" + version: "5.0.2" auto_route_generator: dependency: "direct dev" description: name: auto_route_generator url: "https://pub.dartlang.org" source: hosted - version: "4.2.0" + version: "5.0.3" bloc: - dependency: "direct main" + dependency: transitive description: name: bloc url: "https://pub.dartlang.org" source: hosted - version: "8.0.3" + version: "8.1.0" boolean_selector: dependency: transitive description: @@ -77,7 +77,7 @@ packages: name: build url: "https://pub.dartlang.org" source: hosted - version: "2.3.0" + version: "2.3.1" build_config: dependency: transitive description: @@ -98,14 +98,14 @@ packages: name: build_resolvers url: "https://pub.dartlang.org" source: hosted - version: "2.0.9" + version: "2.1.0" build_runner: dependency: "direct dev" description: name: build_runner url: "https://pub.dartlang.org" source: hosted - version: "2.2.0" + version: "2.2.1" build_runner_core: dependency: transitive description: @@ -140,28 +140,28 @@ packages: name: characters url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" - charcode: + version: "1.2.1" + checked_yaml: dependency: transitive description: - name: charcode + name: checked_yaml url: "https://pub.dartlang.org" source: hosted - version: "1.3.1" - checked_yaml: + version: "2.0.1" + cli_util: dependency: transitive description: - name: checked_yaml + name: cli_util url: "https://pub.dartlang.org" source: hosted - version: "2.0.1" + version: "0.3.5" clock: dependency: transitive description: name: clock url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.1.1" code_builder: dependency: transitive description: @@ -176,6 +176,48 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.16.0" + connectivity_plus: + dependency: transitive + description: + name: connectivity_plus + url: "https://pub.dartlang.org" + source: hosted + version: "2.3.9" + connectivity_plus_linux: + dependency: transitive + description: + name: connectivity_plus_linux + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.1" + connectivity_plus_macos: + dependency: transitive + description: + name: connectivity_plus_macos + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.6" + connectivity_plus_platform_interface: + dependency: transitive + description: + name: connectivity_plus_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.3" + connectivity_plus_web: + dependency: transitive + description: + name: connectivity_plus_web + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.5" + connectivity_plus_windows: + dependency: transitive + description: + name: connectivity_plus_windows + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.2" convert: dependency: transitive description: @@ -210,9 +252,16 @@ packages: name: dart_style url: "https://pub.dartlang.org" source: hosted - version: "2.2.3" + version: "2.2.4" + dbus: + dependency: transitive + description: + name: dbus + url: "https://pub.dartlang.org" + source: hosted + version: "0.7.8" dio: - dependency: "direct main" + dependency: transitive description: name: dio url: "https://pub.dartlang.org" @@ -238,21 +287,21 @@ packages: name: equatable url: "https://pub.dartlang.org" source: hosted - version: "2.0.3" + version: "2.0.5" fake_async: dependency: transitive description: name: fake_async url: "https://pub.dartlang.org" source: hosted - version: "1.3.0" + version: "1.3.1" ffi: dependency: transitive description: name: ffi url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.0.1" file: dependency: transitive description: @@ -278,14 +327,14 @@ packages: name: flutter_bloc url: "https://pub.dartlang.org" source: hosted - version: "8.0.1" + version: "8.1.1" flutter_launcher_icons: dependency: "direct dev" description: name: flutter_launcher_icons url: "https://pub.dartlang.org" source: hosted - version: "0.9.3" + version: "0.10.0" flutter_lints: dependency: "direct dev" description: @@ -304,35 +353,28 @@ packages: name: flutter_native_splash url: "https://pub.dartlang.org" source: hosted - version: "2.2.7" - flutter_screenutil: - dependency: "direct main" - description: - name: flutter_screenutil - url: "https://pub.dartlang.org" - source: hosted - version: "5.5.3+2" + version: "2.2.14" flutter_secure_storage: dependency: "direct main" description: name: flutter_secure_storage url: "https://pub.dartlang.org" source: hosted - version: "5.0.2" + version: "6.0.0" flutter_secure_storage_linux: dependency: transitive description: name: flutter_secure_storage_linux url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.1.1" flutter_secure_storage_macos: dependency: transitive description: name: flutter_secure_storage_macos url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.1.1" flutter_secure_storage_platform_interface: dependency: transitive description: @@ -360,7 +402,7 @@ packages: name: flutter_svg url: "https://pub.dartlang.org" source: hosted - version: "1.1.1+1" + version: "1.1.6" flutter_test: dependency: "direct dev" description: flutter @@ -385,6 +427,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.0" + go_router: + dependency: "direct main" + description: + name: go_router + url: "https://pub.dartlang.org" + source: hosted + version: "5.1.5" graphs: dependency: transitive description: @@ -398,7 +447,7 @@ packages: name: html url: "https://pub.dartlang.org" source: hosted - version: "0.15.0" + version: "0.15.1" http_multi_server: dependency: transitive description: @@ -419,7 +468,7 @@ packages: name: image url: "https://pub.dartlang.org" source: hosted - version: "3.2.0" + version: "3.2.2" intl: dependency: "direct main" description: @@ -447,21 +496,14 @@ packages: name: json_annotation url: "https://pub.dartlang.org" source: hosted - version: "4.6.0" + version: "4.7.0" json_serializable: dependency: "direct dev" description: name: json_serializable url: "https://pub.dartlang.org" source: hosted - version: "6.3.1" - lint: - dependency: transitive - description: - name: lint - url: "https://pub.dartlang.org" - source: hosted - version: "1.8.2" + version: "6.5.4" lints: dependency: transitive description: @@ -483,27 +525,34 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.2" + lottie: + dependency: transitive + description: + name: lottie + url: "https://pub.dartlang.org" + source: hosted + version: "1.4.3" matcher: dependency: transitive description: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.11" + version: "0.12.12" material_color_utilities: dependency: transitive description: name: material_color_utilities url: "https://pub.dartlang.org" source: hosted - version: "0.1.4" + version: "0.1.5" meta: dependency: transitive description: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.7.0" + version: "1.8.0" mime: dependency: transitive description: @@ -518,6 +567,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.0" + nm: + dependency: transitive + description: + name: nm + url: "https://pub.dartlang.org" + source: hosted + version: "0.5.0" package_config: dependency: transitive description: @@ -531,21 +587,42 @@ packages: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.8.1" + version: "1.8.2" path_drawing: dependency: transitive description: name: path_drawing url: "https://pub.dartlang.org" source: hosted - version: "1.0.0" + version: "1.0.1" path_parsing: dependency: transitive description: name: path_parsing url: "https://pub.dartlang.org" source: hosted - version: "1.0.0" + version: "1.0.1" + path_provider: + dependency: transitive + description: + name: path_provider + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.11" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.21" + path_provider_ios: + dependency: transitive + description: + name: path_provider_ios + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.11" path_provider_linux: dependency: transitive description: @@ -553,6 +630,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.7" + path_provider_macos: + dependency: transitive + description: + name: path_provider_macos + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.6" path_provider_platform_interface: dependency: transitive description: @@ -566,7 +650,7 @@ packages: name: path_provider_windows url: "https://pub.dartlang.org" source: hosted - version: "2.1.0" + version: "2.1.3" petitparser: dependency: transitive description: @@ -608,7 +692,7 @@ packages: name: provider url: "https://pub.dartlang.org" source: hosted - version: "6.0.3" + version: "6.0.4" pub_semver: dependency: transitive description: @@ -630,6 +714,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.0.1" + retry: + dependency: transitive + description: + name: retry + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.0" + sembast: + dependency: transitive + description: + name: sembast + url: "https://pub.dartlang.org" + source: hosted + version: "3.3.1" shared_preferences: dependency: transitive description: @@ -700,6 +798,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.2" + sizer: + dependency: "direct main" + description: + name: sizer + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.15" sky_engine: dependency: transitive description: flutter @@ -711,21 +816,21 @@ packages: name: source_gen url: "https://pub.dartlang.org" source: hosted - version: "1.2.2" + version: "1.2.6" source_helper: dependency: transitive description: name: source_helper url: "https://pub.dartlang.org" source: hosted - version: "1.3.2" + version: "1.3.3" source_span: dependency: transitive description: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.8.2" + version: "1.9.0" stack_trace: dependency: transitive description: @@ -753,21 +858,28 @@ packages: name: string_scanner url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.1.1" + synchronized: + dependency: transitive + description: + name: synchronized + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.0+3" term_glyph: dependency: transitive description: name: term_glyph url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "1.2.1" test_api: dependency: transitive description: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.4.9" + version: "0.4.12" timing: dependency: transitive description: @@ -782,13 +894,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.3.1" - universal_html: - dependency: transitive - description: - name: universal_html - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.8" universal_io: dependency: transitive description: @@ -803,6 +908,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.2" + vexana: + dependency: "direct main" + description: + name: vexana + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.1" watcher: dependency: transitive description: @@ -823,7 +935,7 @@ packages: name: win32 url: "https://pub.dartlang.org" source: hosted - version: "2.7.0" + version: "3.1.1" xdg_directories: dependency: transitive description: @@ -846,5 +958,5 @@ packages: source: hosted version: "3.1.1" sdks: - dart: ">=2.17.3 <3.0.0" - flutter: ">=3.0.0" + dart: ">=2.18.0 <3.0.0" + flutter: ">=3.3.0" diff --git a/pubspec.yaml b/pubspec.yaml index eda3cf3..fc34048 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,129 +1,72 @@ name: flutter_mobile_template description: Flutter Mobile App Template -# The following line prevents the package from being accidentally published to -# pub.dev using `flutter pub publish`. This is preferred for private packages. -publish_to: 'none' # Remove this line if you wish to publish to pub.dev - -# 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 +publish_to: 'none' version: 1.0.0+1 environment: sdk: ">=2.17.3 <3.0.0" -# Dependencies specify other packages that your package needs in order to work. -# To automatically upgrade your package dependencies to the latest versions -# consider running `flutter pub upgrade --major-versions`. Alternatively, -# dependencies can be manually updated by changing the version numbers below to -# the latest version available on pub.dev. To see which dependencies have newer -# versions available, run `flutter pub outdated`. dependencies: flutter: sdk: flutter + cupertino_icons: ^1.0.5 + #Responsive + sizer: ^2.0.15 - # The following adds the Cupertino Icons font to your application. - # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^1.0.5 - # Http client - dio: ^4.0.6 - # I18n + #Image + flutter_svg: ^1.1.6 + + #State Management + provider: ^6.0.4 + flutter_bloc: ^8.1.1 + + #JsonAnnotation + json_annotation: ^4.7.0 + equatable: ^2.0.5 + + #Network + vexana: ^3.0.1 + + #Locale Storage + flutter_secure_storage: ^6.0.0 + + #Navigation + go_router: ^5.1.1 + + #Localization intl: ^0.17.0 easy_localization: ^3.0.1 - # Secure String key-value storage - flutter_secure_storage: ^5.0.2 - # Dependency Injection - provider: ^6.0.3 - # State management - flutter_bloc: ^8.0.1 - equatable: ^2.0.3 - bloc: ^8.0.3 - # Navigation - auto_route: ^4.2.1 - # Global in-app alert/notification/toast/snackBar bot_toast: ^4.0.3 - # Responsiveness - flutter_screenutil: ^5.5.3+2 - #SVG Support - flutter_svg: ^1.1.1+1 - # Data Model Json serialization annotations - json_annotation: ^4.6.0 - #Native Splash screen autoconfiguration - flutter_native_splash: ^2.2.7 + #Native Splash Screen + flutter_native_splash: ^2.2.13 dev_dependencies: flutter_test: sdk: flutter - - # The "flutter_lints" package below contains a set of recommended lints to - # encourage good coding practices. The lint set provided by the package is - # activated in the `analysis_options.yaml` file located at the root of your - # package. See that file for information about deactivating specific lint - # rules and activating additional ones. flutter_lints: ^2.0.1 + # Code generation build_runner: ^2.2.0 - auto_route_generator: ^4.2.0 + auto_route_generator: ^5.0.3 + # Json serialization code generation json_serializable: ^6.3.1 + # Native Launcher Icons generator - flutter_launcher_icons: ^0.9.3 + flutter_launcher_icons: ^0.10.0 + # Change app package name/bundle id and app name change_app_package_name: ^1.1.0 rename: ^2.0.1 - -# For information on the generic Dart part of this file, see the -# following page: https://dart.dev/tools/pub/pubspec - -# The following section is specific to Flutter packages. 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 assets to your application, add an assets section, like this: assets: - assets/images/ - assets/icons/ - - assets/translations/ - # - 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 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 + - assets/translations/ \ No newline at end of file diff --git a/lib/core/network/dio_manager.dart b/test/auth/auth_service_test.dart similarity index 100% rename from lib/core/network/dio_manager.dart rename to test/auth/auth_service_test.dart diff --git a/test/widget_test.dart b/test/widget_test.dart index 0c663d9..095fa25 100644 --- a/test/widget_test.dart +++ b/test/widget_test.dart @@ -6,7 +6,6 @@ // tree, read text, and verify that the values of widget properties are correct. import 'package:flutter/material.dart'; -import 'package:flutter_mobile_template/ui/screens/home/home.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_mobile_template/main.dart'; @@ -14,7 +13,7 @@ import 'package:flutter_mobile_template/main.dart'; void main() { testWidgets('Counter increments smoke test', (WidgetTester tester) async { // Build our app and trigger a frame. - await tester.pumpWidget(const HomeScreen()); + await tester.pumpWidget(const MyApp()); // Verify that our counter starts at 0. expect(find.text('0'), findsOneWidget); From 0232c28a1153c7cd17469bf1253d1749143b957c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bedirhan=20Sa=C4=9Flam?= Date: Mon, 21 Nov 2022 16:54:58 +0300 Subject: [PATCH 2/3] #some improvements --- README.md | 14 ++++- .../animated_text/animated_text.dart | 48 ++++++++++++++++ lib/core/components/button/button_widget.dart | 55 +++++++++++++++++++ lib/core/components/popup/main_popup.dart | 13 +++++ lib/core/components/text/custom_text.dart | 24 ++++++++ .../textFormField/text_form_field_widget.dart | 15 +++++ .../textFormField/validate_operations.dart | 25 +++++++++ lib/core/init/routes/routes.dart | 24 +++++++- lib/main.dart | 1 - pubspec.lock | 21 +++++++ pubspec.yaml | 4 ++ 11 files changed, 238 insertions(+), 6 deletions(-) create mode 100644 lib/core/components/animated_text/animated_text.dart create mode 100644 lib/core/components/button/button_widget.dart create mode 100644 lib/core/components/popup/main_popup.dart create mode 100644 lib/core/components/text/custom_text.dart create mode 100644 lib/core/components/textFormField/text_form_field_widget.dart create mode 100644 lib/core/components/textFormField/validate_operations.dart diff --git a/README.md b/README.md index 3cd1caa..fd2a047 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Flutter Mobile App Project Template (flutter_bloc, autoroute, dio, json_serializable, easy_localization bot_toast, flutter_secure_storage(and helpers), flutter_screenutil, flutter_native_splash, flutter_launcher_icons and other generally used features and tools in production.) +# Flutter Mobile App Project Template (flutter_bloc, go_router, vexana, json_serializable, easy_localization bot_toast, flutter_secure_storage(and helpers), sizer, google_fonts, flutter_native_splash, flutter_launcher_icons and other generally used features and tools in production.) This is a battery included [Flutter](https://flutter.dev/) project template. To the default setup have been added: @@ -213,9 +213,15 @@ Main folders structure 📂 state 📂 view 📂 components + 📂 animated_text + 📄 animated_text.dart 📂 button + 📄 button_widget.dart 📂 text + 📄 custom_text.dart 📂 textFormField + 📄 text_form_field_widget.dart + 📄 validate_operations.dart 📂 constants 📂 app 📄 app_constants.dart @@ -254,8 +260,10 @@ Main folders structure 📄 home_view.dart 📄 main.dart -📂 test - +📂 test + 📂 auth + 📄 auth_service_test.dart + 📄 widget_test.dart 📄 flutter_launcher_icons.yaml 📄 flutter_native_splash.yaml diff --git a/lib/core/components/animated_text/animated_text.dart b/lib/core/components/animated_text/animated_text.dart new file mode 100644 index 0000000..65a3f4c --- /dev/null +++ b/lib/core/components/animated_text/animated_text.dart @@ -0,0 +1,48 @@ +import 'package:flutter/material.dart'; + +import '../text/custom_text.dart'; + +class AnimatedText extends StatefulWidget { + const AnimatedText( + this.text, { + super.key, + this.textStyle, + }); + + final String text; + final TextStyle? textStyle; + + @override + State createState() => _AnimatedTextState(); +} + +class _AnimatedTextState extends State + with TickerProviderStateMixin { + late final AnimationController _controller = AnimationController( + duration: const Duration(seconds: 3), + vsync: this, + )..forward(); + late final Animation _animation = CurvedAnimation( + parent: _controller, + curve: Curves.fastOutSlowIn, + ); + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return SizeTransition( + sizeFactor: _animation, + axis: Axis.horizontal, + axisAlignment: -1, + child: CustomText( + widget.text, + textStyle: widget.textStyle, + ), + ); + } +} diff --git a/lib/core/components/button/button_widget.dart b/lib/core/components/button/button_widget.dart new file mode 100644 index 0000000..efba7a5 --- /dev/null +++ b/lib/core/components/button/button_widget.dart @@ -0,0 +1,55 @@ +import 'package:flutter/material.dart'; + +class ButtonWidget extends StatefulWidget { + const ButtonWidget({super.key}); + + @override + State createState() => _ButtonWidgetState(); +} + +class _ButtonWidgetState extends State + with SingleTickerProviderStateMixin { + late double _scale; + late AnimationController _controller; + + @override + void initState() { + super.initState(); + _controller = AnimationController( + vsync: this, + duration: const Duration(milliseconds: 400), + lowerBound: 0.0, + upperBound: 0.1, + )..addListener(() { + setState(() {}); + }); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + void _tapDown(TapDownDetails details) { + _controller.forward(); + } + + void _tapUp(TapUpDetails details) { + _controller.reverse(); + } + + @override + Widget build(BuildContext context) { + _scale = 1 - _controller.value; + return GestureDetector( + onTapDown: _tapDown, + onTapUp: _tapUp, + onTap: () {}, + child: Transform.scale( + scale: _scale, + child: Container(), + ), + ); + } +} diff --git a/lib/core/components/popup/main_popup.dart b/lib/core/components/popup/main_popup.dart new file mode 100644 index 0000000..d1389c8 --- /dev/null +++ b/lib/core/components/popup/main_popup.dart @@ -0,0 +1,13 @@ +import 'package:flutter/material.dart'; + +Future buildGeneralDialog(BuildContext context) { + return showGeneralDialog( + context: context, + pageBuilder: (context, animation, secondaryAnimation) { + return ScaleTransition( + scale: Tween(begin: .5, end: 1).animate(animation), + child: const AlertDialog(), + ); + }, + ); +} diff --git a/lib/core/components/text/custom_text.dart b/lib/core/components/text/custom_text.dart new file mode 100644 index 0000000..3dca8a6 --- /dev/null +++ b/lib/core/components/text/custom_text.dart @@ -0,0 +1,24 @@ +import 'package:auto_size_text/auto_size_text.dart'; +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; + +class CustomText extends StatelessWidget { + final String text; + final TextStyle? textStyle; + final TextAlign? textAlign; + final int? maxLines; + + const CustomText(this.text, + {Key? key, this.textStyle, this.textAlign, this.maxLines}) + : super(key: key); + + @override + Widget build(BuildContext context) { + return AutoSizeText( + text, + style: GoogleFonts.inter(textStyle: textStyle), + textAlign: textAlign, + maxLines: maxLines, + ); + } +} diff --git a/lib/core/components/textFormField/text_form_field_widget.dart b/lib/core/components/textFormField/text_form_field_widget.dart new file mode 100644 index 0000000..91fa5ae --- /dev/null +++ b/lib/core/components/textFormField/text_form_field_widget.dart @@ -0,0 +1,15 @@ +import 'package:flutter/material.dart'; + +class TextFormFieldWidget extends StatefulWidget { + const TextFormFieldWidget({super.key}); + + @override + State createState() => _TextFormFieldWidgetState(); +} + +class _TextFormFieldWidgetState extends State { + @override + Widget build(BuildContext context) { + return Container(); + } +} diff --git a/lib/core/components/textFormField/validate_operations.dart b/lib/core/components/textFormField/validate_operations.dart new file mode 100644 index 0000000..06b3a2e --- /dev/null +++ b/lib/core/components/textFormField/validate_operations.dart @@ -0,0 +1,25 @@ +validateOperation(dynamic value, String errorMessage, + {int minCharacterCount = 0}) { + if (value == null || value.isEmpty || value.length < minCharacterCount) { + return errorMessage; + } + return null; +} + +emailValidateOperation(dynamic email) { + bool emailValid = RegExp( + r"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]+@[a-zA-Z0-9]+\.[a-zA-Z]+") + .hasMatch(email); + + if (email == null || email.isEmpty || !emailValid) { + return "Please enter a valid e-mail address!"; + } + return null; +} + +checkUserNameValidation(bool value) { + if (!value) { + return "*This username is already in use"; + } + return null; +} diff --git a/lib/core/init/routes/routes.dart b/lib/core/init/routes/routes.dart index ba3b5c7..c57b095 100644 --- a/lib/core/init/routes/routes.dart +++ b/lib/core/init/routes/routes.dart @@ -6,13 +6,33 @@ import '../../../view/home/home_view.dart'; final GlobalKey mainNavigatorKey = GlobalKey(debugLabel: 'main'); +animationPage({required GoRouterState state, required Widget route}) => + CustomTransitionPage( + key: state.pageKey, + child: route, + transitionsBuilder: (BuildContext context, Animation animation, + Animation secondaryAnimation, Widget child) => + SlideTransition( + position: animation.drive( + Tween( + begin: const Offset(1, 0), + end: Offset.zero, + ).chain(CurveTween(curve: Curves.fastOutSlowIn)), + ), + child: child, + ), + ); + final routes = GoRouter( initialLocation: '/home', routes: [ GoRoute( path: '/home', - builder: (context, state) { - return const HomeView(); + pageBuilder: (context, state) { + return animationPage( + state: state, + route: const HomeView(), + ); }, ), ], diff --git a/lib/main.dart b/lib/main.dart index 824bf73..1ea4374 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -7,7 +7,6 @@ import 'package:flutter_mobile_template/core/init/routes/routes.dart'; import 'package:flutter_mobile_template/core/init/theme/colors.dart'; import 'package:flutter_mobile_template/view/home/home_view.dart'; import 'package:intl/date_symbol_data_local.dart'; -import 'package:provider/provider.dart'; import 'package:sizer/sizer.dart'; import 'core/base/bloc/app_bloc_observer.dart'; import 'core/dependency_injection.dart'; diff --git a/pubspec.lock b/pubspec.lock index 48c86bb..1eafff3 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -50,6 +50,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "5.0.3" + auto_size_text: + dependency: "direct main" + description: + name: auto_size_text + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.0" bloc: dependency: transitive description: @@ -434,6 +441,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "5.1.5" + google_fonts: + dependency: "direct main" + description: + name: google_fonts + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.1" graphs: dependency: transitive description: @@ -448,6 +462,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.15.1" + http: + dependency: transitive + description: + name: http + url: "https://pub.dartlang.org" + source: hosted + version: "0.13.5" http_multi_server: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index fc34048..65681c4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -12,6 +12,10 @@ dependencies: sdk: flutter cupertino_icons: ^1.0.5 + #Design + google_fonts: ^3.0.1 + auto_size_text: ^3.0.0 + #Responsive sizer: ^2.0.15 From 9c0ade8de94fdfd21370f80c27fa316e13ad7ccf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bedirhan=20Sa=C4=9Flam?= Date: Mon, 21 Nov 2022 16:55:17 +0300 Subject: [PATCH 3/3] #some improvements --- README.md | 36 +++++++-- lib/core/base/bloc/app_bloc_observer.dart | 39 ++++++++- lib/core/base/bloc/auth/auth_bloc.dart | 7 +- lib/core/base/bloc/auth/auth_state.dart | 1 + lib/core/base/functions/base_functions.dart | 79 ++++++++++++++----- lib/core/components/button/button_widget.dart | 6 +- .../textFormField/text_form_field_widget.dart | 2 +- .../textFormField/validate_operations.dart | 4 +- lib/core/constants/app/app_constants.dart | 2 +- .../constants/enums/navigation_enums.dart | 6 ++ .../constants/enums/network_result_enums.dart | 1 + ...njection.dart => dependency_injector.dart} | 0 lib/core/extensions/color_extensions.dart | 1 - lib/core/extensions/context_extensions.dart | 17 ++-- lib/core/extensions/iterable_extensions.dart | 6 ++ lib/core/extensions/string_extensions.dart | 4 +- .../local_storage/local_storage_manager.dart | 46 ++++++----- lib/core/init/main_build/main_build.dart | 21 +++++ lib/core/init/navigation/navigation.dart | 35 ++++++++ .../init/network/network_change_manager.dart | 57 +++++++++++++ lib/core/init/network/no_network_widget.dart | 65 +++++++++++++++ lib/core/init/network/vexana_manager.dart | 8 +- lib/core/init/routes/routes.dart | 39 --------- lib/core/init/theme/app_theme.dart | 14 ++++ lib/core/init/theme/colors.dart | 3 - lib/core/mixins/safe_context_mixin.dart | 14 ++++ lib/core/mixins/state_mixin.dart | 9 +++ lib/core/utility/utility.dart | 10 +++ lib/core/utils.dart | 0 lib/main.dart | 33 ++++---- lib/view/home/bloc/home_bloc.dart | 7 +- lib/view/home/bloc/home_state.dart | 3 +- lib/view/home/home_view.dart | 18 ++++- pubspec.lock | 72 +++++++++++++---- pubspec.yaml | 5 +- scripts/build.sh | 9 +++ 36 files changed, 520 insertions(+), 159 deletions(-) create mode 100644 lib/core/constants/enums/navigation_enums.dart create mode 100644 lib/core/constants/enums/network_result_enums.dart rename lib/core/{dependency_injection.dart => dependency_injector.dart} (100%) create mode 100644 lib/core/init/main_build/main_build.dart create mode 100644 lib/core/init/navigation/navigation.dart create mode 100644 lib/core/init/network/network_change_manager.dart create mode 100644 lib/core/init/network/no_network_widget.dart delete mode 100644 lib/core/init/routes/routes.dart create mode 100644 lib/core/init/theme/app_theme.dart delete mode 100644 lib/core/init/theme/colors.dart create mode 100644 lib/core/mixins/safe_context_mixin.dart create mode 100644 lib/core/mixins/state_mixin.dart create mode 100644 lib/core/utility/utility.dart delete mode 100644 lib/core/utils.dart create mode 100644 scripts/build.sh diff --git a/README.md b/README.md index fd2a047..d0382b9 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Flutter Mobile App Project Template (flutter_bloc, go_router, vexana, json_serializable, easy_localization bot_toast, flutter_secure_storage(and helpers), sizer, google_fonts, flutter_native_splash, flutter_launcher_icons and other generally used features and tools in production.) +# Flutter Mobile App Project Template (flutter_bloc, go_router, vexana, json_serializable, easy_localization bot_toast, flutter_secure_storage, connectivity_plus, sizer, google_fonts, flutter_native_splash, flutter_launcher_icons and other generally used features and tools in production.) This is a battery included [Flutter](https://flutter.dev/) project template. To the default setup have been added: @@ -30,6 +30,10 @@ dependencies: sdk: flutter cupertino_icons: ^1.0.5 + #Text + google_fonts: ^3.0.1 + auto_size_text: ^3.0.0 + #Responsive sizer: ^2.0.15 @@ -46,6 +50,8 @@ dependencies: #Network vexana: ^3.0.1 + url_launcher: ^6.1.6 + connectivity_plus: ^2.3.5 #Locale Storage flutter_secure_storage: ^6.0.0 @@ -68,7 +74,6 @@ dev_dependencies: # Code generation build_runner: ^2.2.0 - auto_route_generator: ^5.0.3 # Json serialization code generation json_serializable: ^6.3.1 @@ -217,6 +222,8 @@ Main folders structure 📄 animated_text.dart 📂 button 📄 button_widget.dart + 📂 popup + 📄 main_popup.dart 📂 text 📄 custom_text.dart 📂 textFormField @@ -227,6 +234,8 @@ Main folders structure 📄 app_constants.dart 📂 enums 📄 api_enums.dart + 📄 navigation_enums.dart + 📄 network_results_enums.dart 📂 extensions 📄 color_extensions.dart 📄 context_extensions.dart @@ -237,14 +246,23 @@ Main folders structure 📂 init 📂 language 📂 local_storage - 📄 local_storage_manager.dart + 📄 local_storage_manager.dart, + 📂 main_build + 📄 main_build.dart + 📂 navigation + 📄 navigation.dart 📂 network + 📄 network_change_manager.dart + 📄 no_network_widget.dart 📄 vexana_manager.dart - 📂 routes - 📄 routes.dart 📂 theme - 📄 colors.dart - 📄 dependency_injection.dart + 📄 app_theme.dart + 📂 mixins + 📄 safe_context_mixin.dart + 📄 state_mixin.dart + 📂 utility + 📄 utility.dart + 📄 dependency_injector.dart 📄 locale_keys.g.dart 📂 view 📂 auth @@ -255,10 +273,12 @@ Main folders structure 📄 {home_state}.dart 📂 model 📂 service - 📄 vexana_manager.dart + 📄 home_service.dart 📂 widgets 📄 home_view.dart 📄 main.dart + 📂 scripts + 📄 build.sh 📂 test 📂 auth diff --git a/lib/core/base/bloc/app_bloc_observer.dart b/lib/core/base/bloc/app_bloc_observer.dart index e67dd33..53f53c4 100644 --- a/lib/core/base/bloc/app_bloc_observer.dart +++ b/lib/core/base/bloc/app_bloc_observer.dart @@ -1,9 +1,42 @@ +import 'dart:developer' as developer; import 'package:flutter_bloc/flutter_bloc.dart'; class AppBlocObserver extends BlocObserver { + static AppBlocObserver? _instance; + static AppBlocObserver get instance { + _instance ??= AppBlocObserver._init(); + return _instance!; + } + + AppBlocObserver._init(); + + @override + void onEvent(Bloc bloc, Object? event) { + super.onEvent(bloc, event); + developer.log(event.toString()); + } + + @override + void onError(BlocBase bloc, Object error, StackTrace stackTrace) { + developer.log(error.toString()); + super.onError(bloc, error, stackTrace); + } + + @override + void onChange(BlocBase bloc, Change change) { + super.onChange(bloc, change); + developer.log(change.toString()); + } + @override - void onCreate(BlocBase bloc) { - // print("OnCreate Bloc: $bloc"); - super.onCreate(bloc); + void onTransition( + Bloc bloc, + Transition transition, + ) { + super.onTransition(bloc, transition); + developer.log(transition.toString()); + developer.log(transition.event); + developer.log(transition.currentState); + developer.log(transition.nextState); } } diff --git a/lib/core/base/bloc/auth/auth_bloc.dart b/lib/core/base/bloc/auth/auth_bloc.dart index bcbec09..fdf06d3 100644 --- a/lib/core/base/bloc/auth/auth_bloc.dart +++ b/lib/core/base/bloc/auth/auth_bloc.dart @@ -1,4 +1,5 @@ -import 'package:bloc/bloc.dart'; +import 'package:flutter/foundation.dart' show immutable; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:equatable/equatable.dart'; part 'auth_event.dart'; @@ -6,8 +7,6 @@ part 'auth_state.dart'; class AuthBloc extends Bloc { AuthBloc() : super(AuthInitial()) { - on((event, emit) { - // TODO: implement event handler - }); + on((event, emit) {}); } } diff --git a/lib/core/base/bloc/auth/auth_state.dart b/lib/core/base/bloc/auth/auth_state.dart index b4ff226..673f1e2 100644 --- a/lib/core/base/bloc/auth/auth_state.dart +++ b/lib/core/base/bloc/auth/auth_state.dart @@ -1,5 +1,6 @@ part of 'auth_bloc.dart'; +@immutable abstract class AuthState extends Equatable { const AuthState(); diff --git a/lib/core/base/functions/base_functions.dart b/lib/core/base/functions/base_functions.dart index 0634e0f..0a19706 100644 --- a/lib/core/base/functions/base_functions.dart +++ b/lib/core/base/functions/base_functions.dart @@ -1,11 +1,14 @@ import 'dart:core'; import 'dart:io'; +import 'dart:developer' as developer; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_mobile_template/core/extensions/string_extensions.dart'; +import 'package:go_router/go_router.dart'; import 'package:intl/intl.dart'; - -import '../../constants/app/app_constants.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import '../../components/text/custom_text.dart'; Widget platformIndicator() { return Center( @@ -15,29 +18,63 @@ Widget platformIndicator() { ); } -Widget platformBackButton({ +IconButton platformBackButton({ required VoidCallback onPressed, - AlignmentGeometry alignment = Alignment.topLeft, - Color? color = AppConstants.black, + Color? color = Colors.black, }) { - return Align( - alignment: alignment, - child: Platform.isIOS - ? IconButton( - onPressed: onPressed, - icon: Icon( - Icons.arrow_back_ios, - color: color, - )) - : IconButton( - onPressed: onPressed, - icon: Icon( - Icons.arrow_back, - color: color, - )), - ); + return Platform.isIOS + ? IconButton( + onPressed: onPressed, + icon: Icon( + Icons.arrow_back_ios, + color: color, + ), + ) + : IconButton( + onPressed: onPressed, + icon: Icon( + Icons.arrow_back, + color: color, + ), + ); +} + +Widget errorText(String errorMessage) { + return Center(child: CustomText(errorMessage)); +} + +logControl(String message) { + developer.log(message); +} + +SvgPicture buildSvgPicture(String path) { + return SvgPicture.asset(path.toSvg); } +Image buildImageAsset(String path) { + return Image.asset(path.toPng); +} + +animatedRouting({ + required GoRouterState state, + required Widget route, +}) => + CustomTransitionPage( + key: state.pageKey, + child: route, + transitionsBuilder: (BuildContext context, Animation animation, + Animation secondaryAnimation, Widget child) => + SlideTransition( + position: animation.drive( + Tween( + begin: const Offset(1, 0), + end: Offset.zero, + ).chain(CurveTween(curve: Curves.fastOutSlowIn)), + ), + child: child, + ), + ); + closePopup(BuildContext context) { Navigator.of(context, rootNavigator: true).pop(); } diff --git a/lib/core/components/button/button_widget.dart b/lib/core/components/button/button_widget.dart index efba7a5..5d46611 100644 --- a/lib/core/components/button/button_widget.dart +++ b/lib/core/components/button/button_widget.dart @@ -1,7 +1,9 @@ import 'package:flutter/material.dart'; class ButtonWidget extends StatefulWidget { - const ButtonWidget({super.key}); + const ButtonWidget({super.key, required this.onTap}); + + final VoidCallback onTap; @override State createState() => _ButtonWidgetState(); @@ -45,7 +47,7 @@ class _ButtonWidgetState extends State return GestureDetector( onTapDown: _tapDown, onTapUp: _tapUp, - onTap: () {}, + onTap: widget.onTap, child: Transform.scale( scale: _scale, child: Container(), diff --git a/lib/core/components/textFormField/text_form_field_widget.dart b/lib/core/components/textFormField/text_form_field_widget.dart index 91fa5ae..e5cef07 100644 --- a/lib/core/components/textFormField/text_form_field_widget.dart +++ b/lib/core/components/textFormField/text_form_field_widget.dart @@ -10,6 +10,6 @@ class TextFormFieldWidget extends StatefulWidget { class _TextFormFieldWidgetState extends State { @override Widget build(BuildContext context) { - return Container(); + return TextFormField(); } } diff --git a/lib/core/components/textFormField/validate_operations.dart b/lib/core/components/textFormField/validate_operations.dart index 06b3a2e..3411eae 100644 --- a/lib/core/components/textFormField/validate_operations.dart +++ b/lib/core/components/textFormField/validate_operations.dart @@ -12,12 +12,12 @@ emailValidateOperation(dynamic email) { .hasMatch(email); if (email == null || email.isEmpty || !emailValid) { - return "Please enter a valid e-mail address!"; + return "Please make sure you enter the correct email."; } return null; } -checkUserNameValidation(bool value) { +checkUserName(bool value) { if (!value) { return "*This username is already in use"; } diff --git a/lib/core/constants/app/app_constants.dart b/lib/core/constants/app/app_constants.dart index 51b4a46..99a9e31 100644 --- a/lib/core/constants/app/app_constants.dart +++ b/lib/core/constants/app/app_constants.dart @@ -9,5 +9,5 @@ class AppConstants { AppConstants._init(); - static const Color black = Color(0x0fffffff); + Color black = const Color(0x0fffffff); } diff --git a/lib/core/constants/enums/navigation_enums.dart b/lib/core/constants/enums/navigation_enums.dart new file mode 100644 index 0000000..9325b1a --- /dev/null +++ b/lib/core/constants/enums/navigation_enums.dart @@ -0,0 +1,6 @@ +enum NavigationEnums { + home('/home'); + + final String routeName; + const NavigationEnums(this.routeName); +} diff --git a/lib/core/constants/enums/network_result_enums.dart b/lib/core/constants/enums/network_result_enums.dart new file mode 100644 index 0000000..153b496 --- /dev/null +++ b/lib/core/constants/enums/network_result_enums.dart @@ -0,0 +1 @@ +enum NetworkResultEnums { on, off } diff --git a/lib/core/dependency_injection.dart b/lib/core/dependency_injector.dart similarity index 100% rename from lib/core/dependency_injection.dart rename to lib/core/dependency_injector.dart diff --git a/lib/core/extensions/color_extensions.dart b/lib/core/extensions/color_extensions.dart index bb663d9..4a0fb4b 100644 --- a/lib/core/extensions/color_extensions.dart +++ b/lib/core/extensions/color_extensions.dart @@ -8,7 +8,6 @@ extension HexColor on Color { return Color(int.parse(buffer.toString(), radix: 16)); } - /// Prefixes a hash sign if [leadingHashSign] is set to `true` (default is `true`). String toHex({bool leadingHashSign = true}) => '${leadingHashSign ? '#' : ''}' '${alpha.toRadixString(16).padLeft(2, '0')}' '${red.toRadixString(16).padLeft(2, '0')}' diff --git a/lib/core/extensions/context_extensions.dart b/lib/core/extensions/context_extensions.dart index 7ea841d..0a02e63 100644 --- a/lib/core/extensions/context_extensions.dart +++ b/lib/core/extensions/context_extensions.dart @@ -1,15 +1,14 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_mobile_template/core/base/bloc/auth/auth_bloc.dart'; +import 'package:flutter_mobile_template/view/home/bloc/home_bloc.dart'; extension ContextExtension on BuildContext { MediaQueryData get mediaQuery => MediaQuery.of(this); - TextTheme get textTheme => Theme.of(this).textTheme; TextTheme get primaryTextTheme => Theme.of(this).primaryTextTheme; - ColorScheme get colorScheme => Theme.of(this).colorScheme; - ThemeData get appTheme => Theme.of(this); - double get textScaleFactor => MediaQuery.of(this).textScaleFactor; } @@ -17,11 +16,11 @@ extension MediaQueryExtension on BuildContext { double get height => mediaQuery.size.height; double get width => mediaQuery.size.width; - double get lowValue => height * 0.01; - double get normalValue => height * 0.02; - double get mediumValue => height * 0.04; - double get highValue => height * 0.1; - double dynamicWidth(double val) => width * val; double dynamicHeight(double val) => height * val; } + +extension BlocExtensions on BuildContext { + AuthBloc get authBloc => read(); + HomeBloc get homeBloc => read(); +} diff --git a/lib/core/extensions/iterable_extensions.dart b/lib/core/extensions/iterable_extensions.dart index c6440da..dd92eff 100644 --- a/lib/core/extensions/iterable_extensions.dart +++ b/lib/core/extensions/iterable_extensions.dart @@ -7,3 +7,9 @@ extension IterableExtension on Iterable { return null; } } + +extension UnaryNumber on List { + bool get isEven => length.isEven; + bool get isOdd => !isEven; + static bool isListEven(List list) => list.isEven; +} diff --git a/lib/core/extensions/string_extensions.dart b/lib/core/extensions/string_extensions.dart index a7f4548..44d7f95 100644 --- a/lib/core/extensions/string_extensions.dart +++ b/lib/core/extensions/string_extensions.dart @@ -3,7 +3,7 @@ extension ImagePath on String { String get toPng => "assets/images/ig_$this.png"; - String get toJpeg => "assets/images/ig_$this.jpg"; + String get toJpg => "assets/images/ig_$this.jpg"; String get toPngIc => "assets/icons/ic_$this.png"; @@ -17,7 +17,7 @@ extension StringValidatorExtension on String? { extension FormValidate on String { String? get emailValidate => - contains("@") ? null : "Uygun bir e-mail adresi giriniz !"; + contains("@") ? null : "Please enter a valid email."; } extension StringExtensions on String { diff --git a/lib/core/init/local_storage/local_storage_manager.dart b/lib/core/init/local_storage/local_storage_manager.dart index f755dc5..fca95fb 100644 --- a/lib/core/init/local_storage/local_storage_manager.dart +++ b/lib/core/init/local_storage/local_storage_manager.dart @@ -3,7 +3,14 @@ import 'dart:convert'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; class LocalStorageManager { - LocalStorageManager._(); + static LocalStorageManager? _instance; + static LocalStorageManager get instance { + if (_instance != null) return _instance!; + _instance = LocalStorageManager._init(); + return _instance!; + } + + LocalStorageManager._init(); static const _androidOptions = AndroidOptions(encryptedSharedPreferences: true); @@ -13,39 +20,39 @@ class LocalStorageManager { static const FlutterSecureStorage _secureStorage = FlutterSecureStorage(iOptions: _iosOptions, aOptions: _androidOptions); - static Future?> getKeys() async => + Future?> getKeys() async => (await _secureStorage.readAll()).keys.toSet(); - static Future containsKey(String key) async => + Future containsKey(String key) async => await _secureStorage.containsKey(key: key); - static Future setString(String key, String value) async => + Future setString(String key, String value) async => await _secureStorage.write(key: key, value: value); - static Future setBool(String key, bool value) async => + Future setBool(String key, bool value) async => await _secureStorage.write(key: key, value: value.toString()); - static Future setNum(String key, num value) async => + Future setNum(String key, num value) async => await _secureStorage.write(key: key, value: value.toString()); - static Future _setList(String key, List value) async { + Future _setList(String key, List value) async { String buffer = json.encode(value); await _secureStorage.write(key: key, value: buffer); } - static Future setStringList(String key, List value) => + Future setStringList(String key, List value) => _setList(key, value); - static Future setNumList(String key, List value) => + Future setNumList(String key, List value) => _setList(key, value); - static Future setBoolList(String key, List value) => + Future setBoolList(String key, List value) => _setList(key, value); - static Future getString(String key) async => + Future getString(String key) async => await _secureStorage.read(key: key); - static Future getBool(String key) async { + Future getBool(String key) async { String? string = await _secureStorage.read(key: key); if (string == null) return null; if (string == 'true') { @@ -57,25 +64,24 @@ class LocalStorageManager { } } - static Future getNum(String key) async => + Future getNum(String key) async => num.tryParse(await _secureStorage.read(key: key) ?? ''); - static Future?> _getList(String key) async { + Future?> _getList(String key) async { String? string = await _secureStorage.read(key: key); if (string == null) return null; List list = (json.decode(string) as List).cast(); return list; } - static Future?> getStringList(String key) => - _getList(key); + Future?> getStringList(String key) => _getList(key); - static Future?> getNumList(String key) => _getList(key); + Future?> getNumList(String key) => _getList(key); - static Future?> getBoolList(String key) => _getList(key); + Future?> getBoolList(String key) => _getList(key); - static Future remove(String key) async => + Future remove(String key) async => await _secureStorage.delete(key: key); - static Future clearAll() async => await _secureStorage.deleteAll(); + Future clearAll() async => await _secureStorage.deleteAll(); } diff --git a/lib/core/init/main_build/main_build.dart b/lib/core/init/main_build/main_build.dart new file mode 100644 index 0000000..ae8cdbd --- /dev/null +++ b/lib/core/init/main_build/main_build.dart @@ -0,0 +1,21 @@ +import 'package:flutter/material.dart'; + +import '../network/no_network_widget.dart'; + +class MainBuild extends StatelessWidget { + const MainBuild({super.key, this.child}); + + final Widget? child; + + @override + Widget build(BuildContext context) { + return Column( + children: [ + Expanded( + child: child ?? const SizedBox.shrink(), + ), + const NoNetworkWidget() + ], + ); + } +} diff --git a/lib/core/init/navigation/navigation.dart b/lib/core/init/navigation/navigation.dart new file mode 100644 index 0000000..45442aa --- /dev/null +++ b/lib/core/init/navigation/navigation.dart @@ -0,0 +1,35 @@ +import 'package:flutter/cupertino.dart'; +import 'package:go_router/go_router.dart'; + +import '../../../view/home/home_view.dart'; +import '../../base/functions/base_functions.dart'; +import '../../constants/enums/navigation_enums.dart'; + +final GlobalKey mainNavigatorKey = + GlobalKey(debugLabel: 'main'); + +class Navigation { + static Navigation? _instance; + static Navigation get instance { + _instance ??= Navigation._init(); + return _instance!; + } + + Navigation._init(); + + final routes = GoRouter( + initialLocation: NavigationEnums.home.routeName, + debugLogDiagnostics: true, + routes: [ + GoRoute( + path: NavigationEnums.home.routeName, + pageBuilder: (context, state) { + return animatedRouting( + state: state, + route: const HomeView(), + ); + }, + ), + ], + ); +} diff --git a/lib/core/init/network/network_change_manager.dart b/lib/core/init/network/network_change_manager.dart new file mode 100644 index 0000000..fd40c14 --- /dev/null +++ b/lib/core/init/network/network_change_manager.dart @@ -0,0 +1,57 @@ +import 'dart:async'; + +import 'package:connectivity_plus/connectivity_plus.dart'; +import 'package:flutter_mobile_template/core/base/functions/base_functions.dart'; + +import '../../constants/enums/network_result_enums.dart'; + +typedef NetworkCallBack = void Function(NetworkResultEnums result); + +abstract class INetworkChangeManager { + Future checkNetworkFirstTime(); + void handleNetworkChange(NetworkCallBack onChange); + void dispose(); +} + +class NetworkChangeManager extends INetworkChangeManager { + late final Connectivity _connectivity; + StreamSubscription? _subscription; + + NetworkChangeManager() { + _connectivity = Connectivity(); + } + + @override + Future checkNetworkFirstTime() async { + final connectivityResult = await (_connectivity.checkConnectivity()); + return checkConnectivityResult(connectivityResult); + } + + @override + void handleNetworkChange(NetworkCallBack onChange) { + _subscription = _connectivity.onConnectivityChanged.listen((event) { + logControl(event.toString()); + onChange.call(checkConnectivityResult(event)); + }); + } + + @override + void dispose() { + _subscription?.cancel(); + } +} + +NetworkResultEnums checkConnectivityResult(ConnectivityResult result) { + switch (result) { + case ConnectivityResult.bluetooth: + case ConnectivityResult.wifi: + case ConnectivityResult.ethernet: + case ConnectivityResult.mobile: + return NetworkResultEnums.on; + case ConnectivityResult.none: + return NetworkResultEnums.off; + case ConnectivityResult.vpn: + break; + } + return NetworkResultEnums.off; +} diff --git a/lib/core/init/network/no_network_widget.dart b/lib/core/init/network/no_network_widget.dart new file mode 100644 index 0000000..fa54ad8 --- /dev/null +++ b/lib/core/init/network/no_network_widget.dart @@ -0,0 +1,65 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_mobile_template/core/components/text/custom_text.dart'; +import 'package:flutter_mobile_template/core/extensions/context_extensions.dart'; + +import '../../constants/enums/network_result_enums.dart'; +import '../../mixins/state_mixin.dart'; +import 'network_change_manager.dart'; + +class NoNetworkWidget extends StatefulWidget { + const NoNetworkWidget({super.key}); + + @override + State createState() => _NoNetworkWidgetState(); +} + +class _NoNetworkWidgetState extends State with StateMixin { + late final INetworkChangeManager _networkChange; + NetworkResultEnums? _networkResult; + + @override + void initState() { + super.initState(); + _networkChange = NetworkChangeManager(); + + waitForScreen(() { + _networkChange.handleNetworkChange((result) { + _updateView(result); + }); + }); + } + + Future fetchFirstResult() async { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + final result = await _networkChange.checkNetworkFirstTime(); + _updateView(result); + }); + } + + void _updateView(NetworkResultEnums result) { + setState(() { + _networkResult = result; + }); + } + + @override + Widget build(BuildContext context) { + return AnimatedCrossFade( + duration: const Duration(seconds: 1), + crossFadeState: _networkResult == NetworkResultEnums.off + ? CrossFadeState.showFirst + : CrossFadeState.showSecond, + firstChild: Material( + color: Colors.white, + child: SizedBox( + height: context.height, + width: double.infinity, + child: const Center( + child: CustomText("No network."), + ), + ), + ), + secondChild: const SizedBox.shrink(), + ); + } +} diff --git a/lib/core/init/network/vexana_manager.dart b/lib/core/init/network/vexana_manager.dart index 84db07b..c01444b 100644 --- a/lib/core/init/network/vexana_manager.dart +++ b/lib/core/init/network/vexana_manager.dart @@ -8,12 +8,16 @@ class VexanaManager { return _instance!; } - static const String baseUrl = "http://localhost:3000/"; + static const String baseUrl = "http://localhost:3000/api"; VexanaManager._init(); INetworkManager networkManager = NetworkManager( isEnableLogger: true, - options: BaseOptions(baseUrl: baseUrl), + isEnableTest: true, + options: BaseOptions( + baseUrl: baseUrl, + followRedirects: true, + ), ); } diff --git a/lib/core/init/routes/routes.dart b/lib/core/init/routes/routes.dart deleted file mode 100644 index c57b095..0000000 --- a/lib/core/init/routes/routes.dart +++ /dev/null @@ -1,39 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:go_router/go_router.dart'; - -import '../../../view/home/home_view.dart'; - -final GlobalKey mainNavigatorKey = - GlobalKey(debugLabel: 'main'); - -animationPage({required GoRouterState state, required Widget route}) => - CustomTransitionPage( - key: state.pageKey, - child: route, - transitionsBuilder: (BuildContext context, Animation animation, - Animation secondaryAnimation, Widget child) => - SlideTransition( - position: animation.drive( - Tween( - begin: const Offset(1, 0), - end: Offset.zero, - ).chain(CurveTween(curve: Curves.fastOutSlowIn)), - ), - child: child, - ), - ); - -final routes = GoRouter( - initialLocation: '/home', - routes: [ - GoRoute( - path: '/home', - pageBuilder: (context, state) { - return animationPage( - state: state, - route: const HomeView(), - ); - }, - ), - ], -); diff --git a/lib/core/init/theme/app_theme.dart b/lib/core/init/theme/app_theme.dart new file mode 100644 index 0000000..561dc25 --- /dev/null +++ b/lib/core/init/theme/app_theme.dart @@ -0,0 +1,14 @@ +import 'package:flutter/material.dart'; + +class AppTheme { + static AppTheme? _instance; + static AppTheme get instance { + if (_instance != null) return _instance!; + _instance = AppTheme._init(); + return _instance!; + } + + AppTheme._init(); + + ThemeData appTheme = ThemeData.dark(); +} diff --git a/lib/core/init/theme/colors.dart b/lib/core/init/theme/colors.dart deleted file mode 100644 index 034fec4..0000000 --- a/lib/core/init/theme/colors.dart +++ /dev/null @@ -1,3 +0,0 @@ -import 'package:flutter/material.dart'; - -const MaterialColor primarySwatch = Colors.blue; diff --git a/lib/core/mixins/safe_context_mixin.dart b/lib/core/mixins/safe_context_mixin.dart new file mode 100644 index 0000000..6f8e6b2 --- /dev/null +++ b/lib/core/mixins/safe_context_mixin.dart @@ -0,0 +1,14 @@ +import 'package:flutter/material.dart'; + +mixin SafeContextMixin on State { + void safeOperation(VoidCallback function) { + if (!mounted) return; + function.call(); + } + + @override + void setState(VoidCallback fn) { + if (!mounted) return; + super.setState(fn); + } +} diff --git a/lib/core/mixins/state_mixin.dart b/lib/core/mixins/state_mixin.dart new file mode 100644 index 0000000..f39a78e --- /dev/null +++ b/lib/core/mixins/state_mixin.dart @@ -0,0 +1,9 @@ +import 'package:flutter/cupertino.dart'; + +mixin StateMixin on State { + void waitForScreen(VoidCallback onComplete) { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + onComplete.call(); + }); + } +} diff --git a/lib/core/utility/utility.dart b/lib/core/utility/utility.dart new file mode 100644 index 0000000..21af409 --- /dev/null +++ b/lib/core/utility/utility.dart @@ -0,0 +1,10 @@ +import 'package:url_launcher/url_launcher.dart'; + +mixin LaunchMixin { + //Mixins should be created for such packages. + launchSocial(Uri url) async { + if (!await launchUrl(url)) { + throw 'Something went wrong! Please try again. $url'; + } + } +} diff --git a/lib/core/utils.dart b/lib/core/utils.dart deleted file mode 100644 index e69de29..0000000 diff --git a/lib/main.dart b/lib/main.dart index 1ea4374..4e10041 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -3,19 +3,17 @@ import 'package:bot_toast/bot_toast.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter/material.dart'; import 'package:easy_localization/easy_localization.dart'; -import 'package:flutter_mobile_template/core/init/routes/routes.dart'; -import 'package:flutter_mobile_template/core/init/theme/colors.dart'; -import 'package:flutter_mobile_template/view/home/home_view.dart'; +import 'package:flutter_mobile_template/core/init/main_build/main_build.dart'; +import 'package:flutter_mobile_template/core/init/navigation/navigation.dart'; +import 'package:flutter_mobile_template/core/init/theme/app_theme.dart'; import 'package:intl/date_symbol_data_local.dart'; import 'package:sizer/sizer.dart'; import 'core/base/bloc/app_bloc_observer.dart'; -import 'core/dependency_injection.dart'; +import 'core/dependency_injector.dart'; void main() async { - WidgetsFlutterBinding.ensureInitialized(); - initializeDateFormatting('tr'); - await EasyLocalization.ensureInitialized(); - + await _init(); + Bloc.observer = AppBlocObserver.instance; runZoned( () => runApp( MultiRepositoryProvider( @@ -27,7 +25,12 @@ void main() async { ), ), ); - Bloc.observer = AppBlocObserver(); +} + +Future _init() async { + WidgetsFlutterBinding.ensureInitialized(); + initializeDateFormatting('tr'); + await EasyLocalization.ensureInitialized(); } class MyApp extends StatelessWidget { @@ -40,16 +43,14 @@ class MyApp extends StatelessWidget { builder: (context, orientation, deviceType) { final botToastBuilder = BotToastInit(); return MaterialApp.router( - routerConfig: routes, + debugShowCheckedModeBanner: false, + title: 'Flutter Mobile App Template', + routerConfig: Navigation.instance.routes, builder: (context, child) => botToastBuilder( context, - const HomeView(), - ), - title: 'Flutter Mobile App Template', - debugShowCheckedModeBanner: false, - theme: ThemeData( - primarySwatch: primarySwatch, + MainBuild(child: child), ), + theme: AppTheme.instance.appTheme, ); }, ); diff --git a/lib/view/home/bloc/home_bloc.dart b/lib/view/home/bloc/home_bloc.dart index ba65316..5e218e0 100644 --- a/lib/view/home/bloc/home_bloc.dart +++ b/lib/view/home/bloc/home_bloc.dart @@ -1,4 +1,5 @@ -import 'package:bloc/bloc.dart'; +import 'package:flutter/foundation.dart' show immutable; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:equatable/equatable.dart'; part 'home_event.dart'; @@ -6,8 +7,6 @@ part 'home_state.dart'; class HomeBloc extends Bloc { HomeBloc() : super(HomeInitial()) { - on((event, emit) { - // TODO: implement event handler - }); + on((event, emit) {}); } } diff --git a/lib/view/home/bloc/home_state.dart b/lib/view/home/bloc/home_state.dart index d87458c..b24c226 100644 --- a/lib/view/home/bloc/home_state.dart +++ b/lib/view/home/bloc/home_state.dart @@ -1,8 +1,9 @@ part of 'home_bloc.dart'; +@immutable abstract class HomeState extends Equatable { const HomeState(); - + @override List get props => []; } diff --git a/lib/view/home/home_view.dart b/lib/view/home/home_view.dart index 221f8dd..f694478 100644 --- a/lib/view/home/home_view.dart +++ b/lib/view/home/home_view.dart @@ -1,13 +1,25 @@ import 'package:flutter/material.dart'; +import 'package:flutter_mobile_template/core/components/text/custom_text.dart'; +import 'package:flutter_mobile_template/core/constants/app/app_constants.dart'; -class HomeView extends StatelessWidget { +class HomeView extends StatefulWidget { const HomeView({super.key}); + @override + State createState() => _HomeViewState(); +} + +class _HomeViewState extends State { @override Widget build(BuildContext context) { return Scaffold( - body: SafeArea( - child: Container(), + body: Center( + child: CustomText( + "Hello World!", + textStyle: TextStyle( + color: AppConstants.instance.black, + ), + ), ), ); } diff --git a/pubspec.lock b/pubspec.lock index 1eafff3..7ec1b65 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -36,20 +36,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.9.0" - auto_route: - dependency: transitive - description: - name: auto_route - url: "https://pub.dartlang.org" - source: hosted - version: "5.0.2" - auto_route_generator: - dependency: "direct dev" - description: - name: auto_route_generator - url: "https://pub.dartlang.org" - source: hosted - version: "5.0.3" auto_size_text: dependency: "direct main" description: @@ -184,7 +170,7 @@ packages: source: hosted version: "1.16.0" connectivity_plus: - dependency: transitive + dependency: "direct main" description: name: connectivity_plus url: "https://pub.dartlang.org" @@ -922,6 +908,62 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.0.4" + url_launcher: + dependency: "direct main" + description: + name: url_launcher + url: "https://pub.dartlang.org" + source: hosted + version: "6.1.6" + url_launcher_android: + dependency: transitive + description: + name: url_launcher_android + url: "https://pub.dartlang.org" + source: hosted + version: "6.0.21" + url_launcher_ios: + dependency: transitive + description: + name: url_launcher_ios + url: "https://pub.dartlang.org" + source: hosted + version: "6.0.17" + url_launcher_linux: + dependency: transitive + description: + name: url_launcher_linux + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.1" + url_launcher_macos: + dependency: transitive + description: + name: url_launcher_macos + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.1" + url_launcher_platform_interface: + dependency: transitive + description: + name: url_launcher_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" + url_launcher_web: + dependency: transitive + description: + name: url_launcher_web + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.13" + url_launcher_windows: + dependency: transitive + description: + name: url_launcher_windows + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.1" vector_math: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 65681c4..109c1df 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -12,7 +12,7 @@ dependencies: sdk: flutter cupertino_icons: ^1.0.5 - #Design + #Text google_fonts: ^3.0.1 auto_size_text: ^3.0.0 @@ -32,6 +32,8 @@ dependencies: #Network vexana: ^3.0.1 + url_launcher: ^6.1.6 + connectivity_plus: ^2.3.5 #Locale Storage flutter_secure_storage: ^6.0.0 @@ -54,7 +56,6 @@ dev_dependencies: # Code generation build_runner: ^2.2.0 - auto_route_generator: ^5.0.3 # Json serialization code generation json_serializable: ^6.3.1 diff --git a/scripts/build.sh b/scripts/build.sh new file mode 100644 index 0000000..b63b681 --- /dev/null +++ b/scripts/build.sh @@ -0,0 +1,9 @@ +flutter pub run build_runner watch + +flutter packages pub run build_runner build --delete-conflicting-outputs + +flutter pub run build_runner build + +flutter pub run flutter_native_splash:create + +flutter pub run flutter_launcher_icons:main \ No newline at end of file