From 3d8929bdb79078f45f3632eadc39d8c57a772329 Mon Sep 17 00:00:00 2001 From: Rody Davis Date: Thu, 6 Jun 2019 13:13:28 -0400 Subject: [PATCH 01/16] updating dep --- ios/Podfile | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++++ pubspec.yaml | 6 ++--- 2 files changed, 72 insertions(+), 3 deletions(-) create mode 100644 ios/Podfile diff --git a/ios/Podfile b/ios/Podfile new file mode 100644 index 00000000..d077b08b --- /dev/null +++ b/ios/Podfile @@ -0,0 +1,69 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '9.0' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def parse_KV_file(file, separator='=') + file_abs_path = File.expand_path(file) + if !File.exists? file_abs_path + return []; + end + pods_ary = [] + skip_line_start_symbols = ["#", "/"] + File.foreach(file_abs_path) { |line| + next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ } + plugin = line.split(pattern=separator) + if plugin.length == 2 + podname = plugin[0].strip() + path = plugin[1].strip() + podpath = File.expand_path("#{path}", file_abs_path) + pods_ary.push({:name => podname, :path => podpath}); + else + puts "Invalid plugin specification: #{line}" + end + } + return pods_ary +end + +target 'Runner' do + # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock + # referring to absolute paths on developers' machines. + system('rm -rf .symlinks') + system('mkdir -p .symlinks/plugins') + + # Flutter Pods + generated_xcode_build_settings = parse_KV_file('./Flutter/Generated.xcconfig') + if generated_xcode_build_settings.empty? + puts "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter packages get is executed first." + end + generated_xcode_build_settings.map { |p| + if p[:name] == 'FLUTTER_FRAMEWORK_DIR' + symlink = File.join('.symlinks', 'flutter') + File.symlink(File.dirname(p[:path]), symlink) + pod 'Flutter', :path => File.join(symlink, File.basename(p[:path])) + end + } + + # Plugin Pods + plugin_pods = parse_KV_file('../.flutter-plugins') + plugin_pods.map { |p| + symlink = File.join('.symlinks', 'plugins', p[:name]) + File.symlink(p[:path], symlink) + pod p[:name], :path => File.join(symlink, 'ios') + } +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + target.build_configurations.each do |config| + config.build_settings['ENABLE_BITCODE'] = 'NO' + end + end +end diff --git a/pubspec.yaml b/pubspec.yaml index 41ab3b53..f4262805 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,14 +15,15 @@ environment: dependencies: flutter: sdk: flutter - + # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^0.1.2 # The following adds the shared pref as a dependency in your application shared_preferences: ^0.4.3 - + # localstorage: ^2.0.0 + # A composable, Future-based library for making HTTP requests. http: ^0.12.0+1 @@ -65,7 +66,6 @@ dev_dependencies: flutter_test: sdk: flutter - flutter_launcher_icons: "^0.7.0" build_runner: ^1.3.0 flutter_icons: From 2e91a60044560f8d97ce3abcc724dc12bfc9e932 Mon Sep 17 00:00:00 2001 From: Rody Davis Date: Thu, 6 Jun 2019 13:17:24 -0400 Subject: [PATCH 02/16] add indexs --- lib/constants/index.dart | 3 +++ lib/data/index.dart | 1 + lib/data/local/constants/index.dart | 1 + lib/data/local/datasources/post/index.dart | 1 + lib/data/local/index.dart | 1 + lib/data/network/apis/posts/index.dart | 1 + lib/data/network/constants/index.dart | 1 + lib/data/network/exceptions/index.dart | 1 + lib/data/network/index.dart | 2 ++ lib/data/sharedpref/constants/index.dart | 1 + lib/models/post/index.dart | 2 ++ lib/ui/home/home.dart | 9 +++++---- lib/ui/login/login.dart | 23 +++++++++++----------- lib/ui/splash/splash.dart | 7 ++++--- lib/utils/dio/index.dart | 1 + lib/utils/encryption/index.dart | 1 + lib/widgets/index.dart | 5 +++++ 17 files changed, 43 insertions(+), 18 deletions(-) create mode 100644 lib/constants/index.dart create mode 100644 lib/data/index.dart create mode 100644 lib/data/local/constants/index.dart create mode 100644 lib/data/local/datasources/post/index.dart create mode 100644 lib/data/local/index.dart create mode 100644 lib/data/network/apis/posts/index.dart create mode 100644 lib/data/network/constants/index.dart create mode 100644 lib/data/network/exceptions/index.dart create mode 100644 lib/data/network/index.dart create mode 100644 lib/data/sharedpref/constants/index.dart create mode 100644 lib/models/post/index.dart create mode 100644 lib/utils/dio/index.dart create mode 100644 lib/utils/encryption/index.dart create mode 100644 lib/widgets/index.dart diff --git a/lib/constants/index.dart b/lib/constants/index.dart new file mode 100644 index 00000000..ee079656 --- /dev/null +++ b/lib/constants/index.dart @@ -0,0 +1,3 @@ +export 'app_theme.dart'; +export 'dimens.dart'; +export 'strings.dart'; diff --git a/lib/data/index.dart b/lib/data/index.dart new file mode 100644 index 00000000..e06c9336 --- /dev/null +++ b/lib/data/index.dart @@ -0,0 +1 @@ +export 'repository.dart'; diff --git a/lib/data/local/constants/index.dart b/lib/data/local/constants/index.dart new file mode 100644 index 00000000..a8f34230 --- /dev/null +++ b/lib/data/local/constants/index.dart @@ -0,0 +1 @@ +export 'db_constants.dart'; diff --git a/lib/data/local/datasources/post/index.dart b/lib/data/local/datasources/post/index.dart new file mode 100644 index 00000000..340edc22 --- /dev/null +++ b/lib/data/local/datasources/post/index.dart @@ -0,0 +1 @@ +export 'post_datasource.dart'; diff --git a/lib/data/local/index.dart b/lib/data/local/index.dart new file mode 100644 index 00000000..b930557d --- /dev/null +++ b/lib/data/local/index.dart @@ -0,0 +1 @@ +export 'app_database.dart'; diff --git a/lib/data/network/apis/posts/index.dart b/lib/data/network/apis/posts/index.dart new file mode 100644 index 00000000..9d952e97 --- /dev/null +++ b/lib/data/network/apis/posts/index.dart @@ -0,0 +1 @@ +export 'post_api.dart'; diff --git a/lib/data/network/constants/index.dart b/lib/data/network/constants/index.dart new file mode 100644 index 00000000..bf657c52 --- /dev/null +++ b/lib/data/network/constants/index.dart @@ -0,0 +1 @@ +export 'endpoints.dart'; diff --git a/lib/data/network/exceptions/index.dart b/lib/data/network/exceptions/index.dart new file mode 100644 index 00000000..608e3305 --- /dev/null +++ b/lib/data/network/exceptions/index.dart @@ -0,0 +1 @@ +export 'network_exceptions.dart'; diff --git a/lib/data/network/index.dart b/lib/data/network/index.dart new file mode 100644 index 00000000..16ebca69 --- /dev/null +++ b/lib/data/network/index.dart @@ -0,0 +1,2 @@ +export 'dio_client.dart'; +export 'rest_client.dart'; diff --git a/lib/data/sharedpref/constants/index.dart b/lib/data/sharedpref/constants/index.dart new file mode 100644 index 00000000..38d4eaf1 --- /dev/null +++ b/lib/data/sharedpref/constants/index.dart @@ -0,0 +1 @@ +export 'preferences.dart'; diff --git a/lib/models/post/index.dart b/lib/models/post/index.dart new file mode 100644 index 00000000..d6c0a6ab --- /dev/null +++ b/lib/models/post/index.dart @@ -0,0 +1,2 @@ +export 'post.dart'; +export 'post_list.dart'; diff --git a/lib/ui/home/home.dart b/lib/ui/home/home.dart index 232ec470..33dc8f97 100644 --- a/lib/ui/home/home.dart +++ b/lib/ui/home/home.dart @@ -1,12 +1,13 @@ -import 'package:boilerplate/data/sharedpref/constants/preferences.dart'; -import 'package:boilerplate/routes.dart'; -import 'package:boilerplate/stores/post/post_store.dart'; -import 'package:boilerplate/widgets/progress_indicator_widget.dart'; import 'package:flushbar/flushbar_helper.dart'; import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import '../../data/sharedpref/constants/preferences.dart'; +import '../../routes.dart'; +import '../../stores/post/post_store.dart'; +import '../../widgets/progress_indicator_widget.dart'; + class HomeScreen extends StatefulWidget { @override _HomeScreenState createState() => _HomeScreenState(); diff --git a/lib/ui/login/login.dart b/lib/ui/login/login.dart index a5b1e6b2..36644d04 100644 --- a/lib/ui/login/login.dart +++ b/lib/ui/login/login.dart @@ -1,16 +1,17 @@ -import 'package:boilerplate/constants/strings.dart'; -import 'package:boilerplate/data/sharedpref/constants/preferences.dart'; -import 'package:boilerplate/routes.dart'; -import 'package:boilerplate/stores/form/form_store.dart'; -import 'package:boilerplate/widgets/app_icon_widget.dart'; -import 'package:boilerplate/widgets/empty_app_bar_widget.dart'; -import 'package:boilerplate/widgets/progress_indicator_widget.dart'; -import 'package:boilerplate/widgets/rounded_button_widget.dart'; -import 'package:boilerplate/widgets/textfield_widget.dart'; +import 'package:flushbar/flushbar_helper.dart'; import 'package:flutter/material.dart'; -import 'package:shared_preferences/shared_preferences.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; -import 'package:flushbar/flushbar_helper.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +import '../../constants/strings.dart'; +import '../../data/sharedpref/constants/preferences.dart'; +import '../../routes.dart'; +import '../../stores/form/form_store.dart'; +import '../../widgets/app_icon_widget.dart'; +import '../../widgets/empty_app_bar_widget.dart'; +import '../../widgets/progress_indicator_widget.dart'; +import '../../widgets/rounded_button_widget.dart'; +import '../../widgets/textfield_widget.dart'; class LoginScreen extends StatefulWidget { @override diff --git a/lib/ui/splash/splash.dart b/lib/ui/splash/splash.dart index e5b616ec..5557324c 100644 --- a/lib/ui/splash/splash.dart +++ b/lib/ui/splash/splash.dart @@ -1,11 +1,12 @@ import 'dart:async'; -import 'package:boilerplate/data/sharedpref/constants/preferences.dart'; -import 'package:boilerplate/routes.dart'; -import 'package:boilerplate/widgets/app_icon_widget.dart'; import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import '../../data/sharedpref/constants/preferences.dart'; +import '../../routes.dart'; +import '../../widgets/app_icon_widget.dart'; + class SplashScreen extends StatefulWidget { @override State createState() => _SplashScreenState(); diff --git a/lib/utils/dio/index.dart b/lib/utils/dio/index.dart new file mode 100644 index 00000000..361976fa --- /dev/null +++ b/lib/utils/dio/index.dart @@ -0,0 +1 @@ +export 'dio_error_util.dart'; diff --git a/lib/utils/encryption/index.dart b/lib/utils/encryption/index.dart new file mode 100644 index 00000000..cfae3b8d --- /dev/null +++ b/lib/utils/encryption/index.dart @@ -0,0 +1 @@ +export 'xxtea.dart'; diff --git a/lib/widgets/index.dart b/lib/widgets/index.dart new file mode 100644 index 00000000..aa5f9793 --- /dev/null +++ b/lib/widgets/index.dart @@ -0,0 +1,5 @@ +export 'app_icon_widget.dart'; +export 'empty_app_bar_widget.dart'; +export 'progress_indicator_widget.dart'; +export 'rounded_button_widget.dart'; +export 'textfield_widget.dart'; From 1bffc07cdeece0b30fdd397ec0c7d11936143e87 Mon Sep 17 00:00:00 2001 From: Rody Davis Date: Thu, 6 Jun 2019 13:22:12 -0400 Subject: [PATCH 03/16] Update splash.dart --- lib/ui/splash/splash.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/ui/splash/splash.dart b/lib/ui/splash/splash.dart index 5557324c..28bc00c5 100644 --- a/lib/ui/splash/splash.dart +++ b/lib/ui/splash/splash.dart @@ -12,6 +12,8 @@ class SplashScreen extends StatefulWidget { State createState() => _SplashScreenState(); } +const bool autoLogin = true; + class _SplashScreenState extends State { @override void initState() { @@ -34,8 +36,8 @@ class _SplashScreenState extends State { navigate() async { SharedPreferences preferences = await SharedPreferences.getInstance(); - if (preferences.getBool(Preferences.is_logged_in) ?? false) { - Navigator.of(context).pushReplacementNamed(Routes.login); + if (autoLogin && preferences.getBool(Preferences.is_logged_in) ?? false) { + Navigator.of(context).pushReplacementNamed(Routes.home); } else { Navigator.of(context).pushReplacementNamed(Routes.login); } From 9d65383a33dab3499e733d8270c9331c84baeb45 Mon Sep 17 00:00:00 2001 From: Rody Davis Date: Thu, 6 Jun 2019 13:25:03 -0400 Subject: [PATCH 04/16] updating strings --- lib/constants/strings.dart | 4 ++++ lib/ui/home/home.dart | 5 +++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/constants/strings.dart b/lib/constants/strings.dart index 2c049d91..f6f618d7 100644 --- a/lib/constants/strings.dart +++ b/lib/constants/strings.dart @@ -9,4 +9,8 @@ class Strings { static const String login_et_user_password = "Enter password"; static const String login_btn_forgot_password = "Forgot Password?"; static const String login_btn_sign_in = "Sign In"; + + //Posts + static const String posts_title = "Posts"; + static const String posts_not_found = "No Posts Found"; } diff --git a/lib/ui/home/home.dart b/lib/ui/home/home.dart index 33dc8f97..5707db22 100644 --- a/lib/ui/home/home.dart +++ b/lib/ui/home/home.dart @@ -1,3 +1,4 @@ +import 'package:boilerplate/constants/index.dart'; import 'package:flushbar/flushbar_helper.dart'; import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; @@ -35,7 +36,7 @@ class _HomeScreenState extends State { Widget _buildAppBar(BuildContext context) { return AppBar( - title: Text('Posts'), + title: Text(Strings.posts_title), actions: [ IconButton( onPressed: () { @@ -100,7 +101,7 @@ class _HomeScreenState extends State { ); }, ) - : Center(child: Text('No posts found')); + : Center(child: Text(Strings.posts_not_found)); } // General Methods:----------------------------------------------------------- From 778e63ab3906609d27a41cf023a7902e3d62243a Mon Sep 17 00:00:00 2001 From: Rody Davis Date: Thu, 6 Jun 2019 13:26:14 -0400 Subject: [PATCH 05/16] Updating Strings --- lib/constants/strings.dart | 15 ++++++++------- lib/ui/login/login.dart | 2 +- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/lib/constants/strings.dart b/lib/constants/strings.dart index f6f618d7..f686393d 100644 --- a/lib/constants/strings.dart +++ b/lib/constants/strings.dart @@ -2,15 +2,16 @@ class Strings { Strings._(); //General - static const String appName = "Boilerplate Project"; + static const String appName = 'Boilerplate Project'; //Login - static const String login_et_user_email = "Enter user email"; - static const String login_et_user_password = "Enter password"; - static const String login_btn_forgot_password = "Forgot Password?"; - static const String login_btn_sign_in = "Sign In"; + static const String login_et_user_email = 'Enter user email'; + static const String login_et_user_password = 'Enter password'; + static const String login_btn_forgot_password = 'Forgot Password?'; + static const String login_btn_sign_in = 'Sign In'; + static const String login_validation_error = 'Please fill in all fields'; //Posts - static const String posts_title = "Posts"; - static const String posts_not_found = "No Posts Found"; + static const String posts_title = 'Posts'; + static const String posts_not_found = 'No Posts Found'; } diff --git a/lib/ui/login/login.dart b/lib/ui/login/login.dart index 36644d04..6bd26482 100644 --- a/lib/ui/login/login.dart +++ b/lib/ui/login/login.dart @@ -212,7 +212,7 @@ class _LoginScreenState extends State { if (_store.canLogin) { _store.login(); } else { - showErrorMessage(context, 'Please fill in all fields'); + showErrorMessage(context, Strings.login_validation_error); } }, ); From f6310312666d5fa728e8142c460d9d52335abea0 Mon Sep 17 00:00:00 2001 From: Rody Davis Date: Thu, 6 Jun 2019 13:32:23 -0400 Subject: [PATCH 06/16] Update home.dart --- lib/ui/home/home.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ui/home/home.dart b/lib/ui/home/home.dart index 5707db22..d1f0d9e3 100644 --- a/lib/ui/home/home.dart +++ b/lib/ui/home/home.dart @@ -1,9 +1,9 @@ -import 'package:boilerplate/constants/index.dart'; import 'package:flushbar/flushbar_helper.dart'; import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import '../../constants/index.dart'; import '../../data/sharedpref/constants/preferences.dart'; import '../../routes.dart'; import '../../stores/post/post_store.dart'; From a842bb00bf0bc2c1545d40a90b02b36bcec315ee Mon Sep 17 00:00:00 2001 From: Rody Davis Date: Thu, 6 Jun 2019 13:34:13 -0400 Subject: [PATCH 07/16] setting screen --- .../xcshareddata/WorkspaceSettings.xcsettings | 8 ++++++++ lib/ui/settings/settings.dart | 11 +++++++++++ 2 files changed, 19 insertions(+) create mode 100644 ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 lib/ui/settings/settings.dart diff --git a/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 00000000..949b6789 --- /dev/null +++ b/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + BuildSystemType + Original + + diff --git a/lib/ui/settings/settings.dart b/lib/ui/settings/settings.dart new file mode 100644 index 00000000..87b02c67 --- /dev/null +++ b/lib/ui/settings/settings.dart @@ -0,0 +1,11 @@ +import 'package:boilerplate/widgets/index.dart'; +import 'package:flutter/material.dart'; + +class SettingsScreen extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: EmptyAppBar(), + ); + } +} From cf89fe7ca98023d7044ac9b7549f2bdf9f3e74e2 Mon Sep 17 00:00:00 2001 From: Rody Davis Date: Thu, 6 Jun 2019 13:57:37 -0400 Subject: [PATCH 08/16] Adding Locale Support --- lib/constants/index.dart | 1 - lib/constants/strings.dart | 17 ------ lib/locale/index.dart | 1 + lib/locale/localizations.dart | 102 ++++++++++++++++++++++++++++++++++ lib/main.dart | 15 ++++- lib/ui/home/home.dart | 6 +- lib/ui/login/login.dart | 12 ++-- pubspec.yaml | 2 + 8 files changed, 126 insertions(+), 30 deletions(-) delete mode 100644 lib/constants/strings.dart create mode 100644 lib/locale/index.dart create mode 100644 lib/locale/localizations.dart diff --git a/lib/constants/index.dart b/lib/constants/index.dart index ee079656..961e1f8c 100644 --- a/lib/constants/index.dart +++ b/lib/constants/index.dart @@ -1,3 +1,2 @@ export 'app_theme.dart'; export 'dimens.dart'; -export 'strings.dart'; diff --git a/lib/constants/strings.dart b/lib/constants/strings.dart deleted file mode 100644 index f686393d..00000000 --- a/lib/constants/strings.dart +++ /dev/null @@ -1,17 +0,0 @@ -class Strings { - Strings._(); - - //General - static const String appName = 'Boilerplate Project'; - - //Login - static const String login_et_user_email = 'Enter user email'; - static const String login_et_user_password = 'Enter password'; - static const String login_btn_forgot_password = 'Forgot Password?'; - static const String login_btn_sign_in = 'Sign In'; - static const String login_validation_error = 'Please fill in all fields'; - - //Posts - static const String posts_title = 'Posts'; - static const String posts_not_found = 'No Posts Found'; -} diff --git a/lib/locale/index.dart b/lib/locale/index.dart new file mode 100644 index 00000000..e51b0a22 --- /dev/null +++ b/lib/locale/index.dart @@ -0,0 +1 @@ +export 'localizations.dart'; diff --git a/lib/locale/localizations.dart b/lib/locale/localizations.dart new file mode 100644 index 00000000..63765296 --- /dev/null +++ b/lib/locale/localizations.dart @@ -0,0 +1,102 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// A simple "rough and ready" example of localizing a Flutter app. +// Spanish and English (locale language codes 'en' and 'es') are +// supported. + +// The pubspec.yaml file must include flutter_localizations in its +// dependencies section. For example: +// +// dependencies: +// flutter: +// sdk: flutter +// flutter_localizations: +// sdk: flutter + +// If you run this app with the device's locale set to anything but +// English or Spanish, the app's locale will be English. If you +// set the device's locale to Spanish, the app's locale will be +// Spanish. + +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter/foundation.dart' show SynchronousFuture; + +class AppLocalizations { + AppLocalizations(this.locale); + + final Locale locale; + + static AppLocalizations of(BuildContext context) { + return Localizations.of(context, AppLocalizations); + } + + static Map> _localizedValues = { + 'en': { + 'title': 'Boilerplate Project', + 'login_et_user_email': 'Enter user email', + 'login_et_user_password': 'Enter password', + 'login_btn_forgot_password': 'Forgot Password?', + 'login_btn_sign_in': 'Sign In', + 'login_validation_error': 'Please fill in all fields', + 'posts_title': 'Posts', + 'posts_not_found': 'No Posts Found', + }, + 'es': { + 'title': 'Proyecto repetitivo', + 'login_et_user_email': 'Ingrese el email del usuario', + 'login_et_user_password': 'introducir la contraseña', + 'login_btn_forgot_password': 'Se te olvidó tu contraseña', + 'login_btn_sign_in': 'Registrarse', + 'login_validation_error': 'Por favor rellena todos los campos', + 'posts_title': 'Mensajes', + 'posts_not_found': 'No se han encontrado publicacionesd', + }, + }; + + String get title => _localizedValues[locale.languageCode]['title']; + String get login_et_user_email => + _localizedValues[locale.languageCode]['login_et_user_email']; + String get login_et_user_password => + _localizedValues[locale.languageCode]['login_et_user_password']; + String get login_btn_forgot_password => + _localizedValues[locale.languageCode]['login_btn_forgot_password']; + String get login_btn_sign_in => + _localizedValues[locale.languageCode]['login_btn_sign_in']; + String get login_validation_error => + _localizedValues[locale.languageCode]['login_validation_error']; + String get posts_title => + _localizedValues[locale.languageCode]['posts_title']; + String get posts_not_found => + _localizedValues[locale.languageCode]['posts_not_found']; +} + +class AppLocalizationsDelegate extends LocalizationsDelegate { + const AppLocalizationsDelegate(); + + static List get supportedLocales => [ + Locale('en', ''), + Locale('es', ''), + ]; + + @override + bool isSupported(Locale locale) { + return supportedLocales + .map((l) => l.languageCode) + .toList() + .contains(locale.languageCode); + } + + @override + Future load(Locale locale) { + // Returning a SynchronousFuture here because an async "load" operation + // isn't needed to produce an instance of AppLocalizations. + return SynchronousFuture(AppLocalizations(locale)); + } + + @override + bool shouldReload(AppLocalizationsDelegate old) => false; +} diff --git a/lib/main.dart b/lib/main.dart index 5e2ab6ad..98e07fe9 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,9 +1,10 @@ -import 'package:boilerplate/routes.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; import 'constants/app_theme.dart'; -import 'constants/strings.dart'; +import 'locale/index.dart'; +import 'routes.dart'; import 'ui/splash/splash.dart'; void main() { @@ -22,8 +23,16 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( + localizationsDelegates: [ + // ... app-specific localization delegate[s] here + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + const AppLocalizationsDelegate(), + ], + supportedLocales: AppLocalizationsDelegate.supportedLocales, debugShowCheckedModeBanner: false, - title: Strings.appName, + onGenerateTitle: (BuildContext context) => + AppLocalizations.of(context).title, theme: themeData, routes: Routes.routes, home: SplashScreen(), diff --git a/lib/ui/home/home.dart b/lib/ui/home/home.dart index d1f0d9e3..f3a8a660 100644 --- a/lib/ui/home/home.dart +++ b/lib/ui/home/home.dart @@ -3,8 +3,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:shared_preferences/shared_preferences.dart'; -import '../../constants/index.dart'; import '../../data/sharedpref/constants/preferences.dart'; +import '../../locale/index.dart'; import '../../routes.dart'; import '../../stores/post/post_store.dart'; import '../../widgets/progress_indicator_widget.dart'; @@ -36,7 +36,7 @@ class _HomeScreenState extends State { Widget _buildAppBar(BuildContext context) { return AppBar( - title: Text(Strings.posts_title), + title: Text(AppLocalizations.of(context).posts_title), actions: [ IconButton( onPressed: () { @@ -101,7 +101,7 @@ class _HomeScreenState extends State { ); }, ) - : Center(child: Text(Strings.posts_not_found)); + : Center(child: Text(AppLocalizations.of(context).posts_not_found)); } // General Methods:----------------------------------------------------------- diff --git a/lib/ui/login/login.dart b/lib/ui/login/login.dart index 6bd26482..237df7c0 100644 --- a/lib/ui/login/login.dart +++ b/lib/ui/login/login.dart @@ -3,8 +3,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:shared_preferences/shared_preferences.dart'; -import '../../constants/strings.dart'; import '../../data/sharedpref/constants/preferences.dart'; +import '../../locale/index.dart'; import '../../routes.dart'; import '../../stores/form/form_store.dart'; import '../../widgets/app_icon_widget.dart'; @@ -154,7 +154,7 @@ class _LoginScreenState extends State { return Observer( builder: (context) { return TextFieldWidget( - hint: Strings.login_et_user_email, + hint: AppLocalizations.of(context).login_et_user_email, inputType: TextInputType.emailAddress, icon: Icons.person, iconColor: Colors.black54, @@ -173,7 +173,7 @@ class _LoginScreenState extends State { return Observer( builder: (context) { return TextFieldWidget( - hint: Strings.login_et_user_password, + hint: AppLocalizations.of(context).login_et_user_password, isObscure: true, padding: EdgeInsets.only(top: 16.0), icon: Icons.lock, @@ -192,7 +192,7 @@ class _LoginScreenState extends State { child: FlatButton( padding: EdgeInsets.all(0.0), child: Text( - Strings.login_btn_forgot_password, + AppLocalizations.of(context).login_btn_forgot_password, style: Theme.of(context) .textTheme .caption @@ -205,14 +205,14 @@ class _LoginScreenState extends State { Widget _buildSignInButton() { return RoundedButtonWidget( - buttonText: Strings.login_btn_sign_in, + buttonText: AppLocalizations.of(context).login_btn_sign_in, buttonColor: Colors.orangeAccent, textColor: Colors.white, onPressed: () async { if (_store.canLogin) { _store.login(); } else { - showErrorMessage(context, Strings.login_validation_error); + showErrorMessage(context, AppLocalizations.of(context).login_validation_error); } }, ); diff --git a/pubspec.yaml b/pubspec.yaml index f4262805..4b7d7aa0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,6 +15,8 @@ environment: dependencies: flutter: sdk: flutter + flutter_localizations: + sdk: flutter # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. From 04a5222d202562cec968086ae623d20c7b376d71 Mon Sep 17 00:00:00 2001 From: Rody Davis Date: Thu, 6 Jun 2019 14:01:22 -0400 Subject: [PATCH 09/16] Update main.dart --- lib/main.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 98e07fe9..e6b8c902 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -31,8 +31,9 @@ class MyApp extends StatelessWidget { ], supportedLocales: AppLocalizationsDelegate.supportedLocales, debugShowCheckedModeBanner: false, - onGenerateTitle: (BuildContext context) => - AppLocalizations.of(context).title, + onGenerateTitle: (BuildContext context) { + return AppLocalizations.of(context).title; + }, theme: themeData, routes: Routes.routes, home: SplashScreen(), From ce0a564ccc579e636c80f033c176e38245749c73 Mon Sep 17 00:00:00 2001 From: Rody Davis Date: Thu, 6 Jun 2019 14:06:57 -0400 Subject: [PATCH 10/16] Update pubspec.yaml --- pubspec.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 4b7d7aa0..8643d52a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -24,7 +24,7 @@ dependencies: # The following adds the shared pref as a dependency in your application shared_preferences: ^0.4.3 - # localstorage: ^2.0.0 + localstorage: ^2.0.0 # A composable, Future-based library for making HTTP requests. http: ^0.12.0+1 @@ -63,6 +63,9 @@ dependencies: # A flexible widget for user notification. flushbar: 1.5.3 +dependency_overrides: + f_logs: + git: "https://github.com/AppleEducate/Flogs" dev_dependencies: flutter_test: From 780c1419e2eb60ecc68fb463c33f572e3f5bfd23 Mon Sep 17 00:00:00 2001 From: Rody Davis Date: Thu, 6 Jun 2019 14:12:35 -0400 Subject: [PATCH 11/16] Dark Mode Support --- lib/constants/app_theme.dart | 19 +++++++++++++------ lib/main.dart | 1 + 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/lib/constants/app_theme.dart b/lib/constants/app_theme.dart index 531d7dfc..fb2884be 100644 --- a/lib/constants/app_theme.dart +++ b/lib/constants/app_theme.dart @@ -23,16 +23,23 @@ import 'package:flutter/material.dart'; -final ThemeData themeData = new ThemeData( +final ThemeData themeData = ThemeData( fontFamily: 'ProductSans', brightness: Brightness.light, - primarySwatch: MaterialColor( - AppColors.green[500].value, AppColors.green), + primarySwatch: MaterialColor(AppColors.green[500].value, AppColors.green), primaryColor: AppColors.green[500], primaryColorBrightness: Brightness.light, accentColor: AppColors.green[500], - accentColorBrightness: Brightness.light -); + accentColorBrightness: Brightness.light); + +final ThemeData themeDataDark = ThemeData( + fontFamily: 'ProductSans', + brightness: Brightness.dark, + primarySwatch: MaterialColor(AppColors.green[900].value, AppColors.green), + primaryColor: AppColors.green[500], + primaryColorBrightness: Brightness.dark, + accentColor: AppColors.green[500], + accentColorBrightness: Brightness.dark); class AppColors { AppColors._(); // this basically makes it so you can instantiate this class @@ -49,4 +56,4 @@ class AppColors { 800: const Color(0xFF76af60), 900: const Color(0xFF64a24d) }; -} \ No newline at end of file +} diff --git a/lib/main.dart b/lib/main.dart index e6b8c902..77f12972 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -35,6 +35,7 @@ class MyApp extends StatelessWidget { return AppLocalizations.of(context).title; }, theme: themeData, + darkTheme: themeDataDark, routes: Routes.routes, home: SplashScreen(), ); From c47cf263baa1502bea697cec6d0e25ff1807cc9d Mon Sep 17 00:00:00 2001 From: Rody Davis Date: Thu, 6 Jun 2019 14:22:56 -0400 Subject: [PATCH 12/16] adding bottom navigation --- lib/routes.dart | 18 ++++++++--- lib/ui/navigation.dart | 69 ++++++++++++++++++++++++++++++++++++++++++ pubspec.yaml | 2 +- 3 files changed, 84 insertions(+), 5 deletions(-) create mode 100644 lib/ui/navigation.dart diff --git a/lib/routes.dart b/lib/routes.dart index b4fbe8bb..31a30ef3 100644 --- a/lib/routes.dart +++ b/lib/routes.dart @@ -2,6 +2,8 @@ import 'package:flutter/material.dart'; import 'ui/home/home.dart'; import 'ui/login/login.dart'; +import 'ui/navigation.dart'; +import 'ui/settings/settings.dart'; import 'ui/splash/splash.dart'; class Routes { @@ -15,9 +17,17 @@ class Routes { static final routes = { splash: (BuildContext context) => SplashScreen(), login: (BuildContext context) => LoginScreen(), - home: (BuildContext context) => HomeScreen(), + home: (BuildContext context) => AppNavigation(children: [ + Screen( + iconData: Icons.home, + title: 'Home', + child: HomeScreen(), + ), + Screen( + iconData: Icons.settings, + title: 'Settings', + child: SettingsScreen(), + ), + ]), }; } - - - diff --git a/lib/ui/navigation.dart b/lib/ui/navigation.dart new file mode 100644 index 00000000..4b14c684 --- /dev/null +++ b/lib/ui/navigation.dart @@ -0,0 +1,69 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; + +class AppNavigation extends StatefulWidget { + AppNavigation({ + @required this.children, + }); + final List children; + @override + _AppNavigationState createState() => _AppNavigationState(); +} + +class _AppNavigationState extends State { + int _currentIndex = 0; + @override + Widget build(BuildContext context) { + return Scaffold( + body: Stack( + children: [ + for (int i = 0; i < widget.children.length; i++) ...[ + Offstage( + offstage: _currentIndex != i, + child: widget.children[i].child, + ), + ], + ], + ), + bottomNavigationBar: BottomNavigationBar( + currentIndex: _currentIndex, + onTap: tabSelected, + items: [ + for (var tab in widget.children) ...[ + BottomNavigationBarItem( + icon: Icon(tab.icon), + title: Text(tab.title), + ), + ], + ], + ), + ); + } + + void tabSelected(val) { + if (mounted) + setState(() { + _currentIndex = val; + }); + } +} + +class Screen { + const Screen({ + @required this.title, + @required this.iconData, + @required this.child, + this.description, + this.iosIconData, + }); + final Widget child; + final String title, description; + final IconData iconData, iosIconData; + IconData get icon { + if (Platform.isIOS || Platform.isMacOS) { + return iosIconData ?? iconData; + } + return iconData; + } +} diff --git a/pubspec.yaml b/pubspec.yaml index 8643d52a..c525d377 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -10,7 +10,7 @@ description: A flutter boilerplate project created using MobX and Provider. version: 1.0.0+1 environment: - sdk: ">=2.1.0 <3.0.0" + sdk: ">=2.2.2 <3.0.0" dependencies: flutter: From 87dc76d0784363aa4950eeacd21cc81156d5e05b Mon Sep 17 00:00:00 2001 From: Rody Davis Date: Thu, 6 Jun 2019 14:51:39 -0400 Subject: [PATCH 13/16] adding dynamic navigaiton --- lib/locale/localizations.dart | 4 + lib/routes.dart | 13 +-- lib/ui/home/home.dart | 26 ------ lib/ui/navigation.dart | 95 +++++++------------- lib/ui/settings/settings.dart | 5 +- lib/widgets/index.dart | 1 + lib/widgets/navigation.dart | 162 ++++++++++++++++++++++++++++++++++ 7 files changed, 203 insertions(+), 103 deletions(-) create mode 100644 lib/widgets/navigation.dart diff --git a/lib/locale/localizations.dart b/lib/locale/localizations.dart index 63765296..4d3e90f9 100644 --- a/lib/locale/localizations.dart +++ b/lib/locale/localizations.dart @@ -44,6 +44,7 @@ class AppLocalizations { 'login_validation_error': 'Please fill in all fields', 'posts_title': 'Posts', 'posts_not_found': 'No Posts Found', + 'settings_title': 'Settings', }, 'es': { 'title': 'Proyecto repetitivo', @@ -54,6 +55,7 @@ class AppLocalizations { 'login_validation_error': 'Por favor rellena todos los campos', 'posts_title': 'Mensajes', 'posts_not_found': 'No se han encontrado publicacionesd', + 'settings_title': 'Ajustes', }, }; @@ -72,6 +74,8 @@ class AppLocalizations { _localizedValues[locale.languageCode]['posts_title']; String get posts_not_found => _localizedValues[locale.languageCode]['posts_not_found']; + String get settings_title => + _localizedValues[locale.languageCode]['settings_title']; } class AppLocalizationsDelegate extends LocalizationsDelegate { diff --git a/lib/routes.dart b/lib/routes.dart index 31a30ef3..1694def1 100644 --- a/lib/routes.dart +++ b/lib/routes.dart @@ -17,17 +17,6 @@ class Routes { static final routes = { splash: (BuildContext context) => SplashScreen(), login: (BuildContext context) => LoginScreen(), - home: (BuildContext context) => AppNavigation(children: [ - Screen( - iconData: Icons.home, - title: 'Home', - child: HomeScreen(), - ), - Screen( - iconData: Icons.settings, - title: 'Settings', - child: SettingsScreen(), - ), - ]), + home: (BuildContext context) => AppNavigation(), }; } diff --git a/lib/ui/home/home.dart b/lib/ui/home/home.dart index f3a8a660..bd4702e1 100644 --- a/lib/ui/home/home.dart +++ b/lib/ui/home/home.dart @@ -28,32 +28,6 @@ class _HomeScreenState extends State { @override Widget build(BuildContext context) { - return Scaffold( - appBar: _buildAppBar(context), - body: _buildBody(), - ); - } - - Widget _buildAppBar(BuildContext context) { - return AppBar( - title: Text(AppLocalizations.of(context).posts_title), - actions: [ - IconButton( - onPressed: () { - SharedPreferences.getInstance().then((preference) { - preference.setBool(Preferences.is_logged_in, false); - Navigator.of(context).pushReplacementNamed(Routes.login); - }); - }, - icon: Icon( - Icons.power_settings_new, - ), - ) - ], - ); - } - - Widget _buildBody() { return Stack( children: [ Observer( diff --git a/lib/ui/navigation.dart b/lib/ui/navigation.dart index 4b14c684..50b515b1 100644 --- a/lib/ui/navigation.dart +++ b/lib/ui/navigation.dart @@ -1,69 +1,42 @@ -import 'dart:io'; - +import 'package:boilerplate/locale/index.dart'; import 'package:flutter/material.dart'; +import 'package:shared_preferences/shared_preferences.dart'; -class AppNavigation extends StatefulWidget { - AppNavigation({ - @required this.children, - }); - final List children; - @override - _AppNavigationState createState() => _AppNavigationState(); -} +import '../data/sharedpref/constants/index.dart'; +import '../routes.dart'; +import '../widgets/index.dart'; +import 'home/home.dart'; +import 'settings/settings.dart'; -class _AppNavigationState extends State { - int _currentIndex = 0; +class AppNavigation extends StatelessWidget { @override Widget build(BuildContext context) { - return Scaffold( - body: Stack( - children: [ - for (int i = 0; i < widget.children.length; i++) ...[ - Offstage( - offstage: _currentIndex != i, - child: widget.children[i].child, - ), - ], - ], - ), - bottomNavigationBar: BottomNavigationBar( - currentIndex: _currentIndex, - onTap: tabSelected, - items: [ - for (var tab in widget.children) ...[ - BottomNavigationBarItem( - icon: Icon(tab.icon), - title: Text(tab.title), - ), - ], - ], - ), + return DynamicNavigation( + type: NavigationType.bottomTabs, + children: [ + Screen( + iconData: Icons.home, + title: AppLocalizations.of(context).posts_title, + child: HomeScreen(), + actions: [ + IconButton( + onPressed: () { + SharedPreferences.getInstance().then((preference) { + preference.setBool(Preferences.is_logged_in, false); + Navigator.of(context).pushReplacementNamed(Routes.login); + }); + }, + icon: Icon( + Icons.power_settings_new, + ), + ), + ]), + Screen( + iconData: Icons.settings, + title: AppLocalizations.of(context).settings_title, + child: SettingsScreen(), + ), + ], ); } - - void tabSelected(val) { - if (mounted) - setState(() { - _currentIndex = val; - }); - } -} - -class Screen { - const Screen({ - @required this.title, - @required this.iconData, - @required this.child, - this.description, - this.iosIconData, - }); - final Widget child; - final String title, description; - final IconData iconData, iosIconData; - IconData get icon { - if (Platform.isIOS || Platform.isMacOS) { - return iosIconData ?? iconData; - } - return iconData; - } } diff --git a/lib/ui/settings/settings.dart b/lib/ui/settings/settings.dart index 87b02c67..a537c7b0 100644 --- a/lib/ui/settings/settings.dart +++ b/lib/ui/settings/settings.dart @@ -1,11 +1,8 @@ -import 'package:boilerplate/widgets/index.dart'; import 'package:flutter/material.dart'; class SettingsScreen extends StatelessWidget { @override Widget build(BuildContext context) { - return Scaffold( - appBar: EmptyAppBar(), - ); + return Container(); } } diff --git a/lib/widgets/index.dart b/lib/widgets/index.dart index aa5f9793..d0ac5d01 100644 --- a/lib/widgets/index.dart +++ b/lib/widgets/index.dart @@ -1,5 +1,6 @@ export 'app_icon_widget.dart'; export 'empty_app_bar_widget.dart'; +export 'navigation.dart'; export 'progress_indicator_widget.dart'; export 'rounded_button_widget.dart'; export 'textfield_widget.dart'; diff --git a/lib/widgets/navigation.dart b/lib/widgets/navigation.dart new file mode 100644 index 00000000..9d9d6bdc --- /dev/null +++ b/lib/widgets/navigation.dart @@ -0,0 +1,162 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; + +class DynamicNavigation extends StatefulWidget { + DynamicNavigation({ + @required this.children, + this.type = NavigationType.bottomTabs, + }); + + final List children; + final NavigationType type; + + @override + _DynamicNavigationState createState() => _DynamicNavigationState(); +} + +class _DynamicNavigationState extends State { + int _currentIndex = 0; + + void tabSelected(val) { + if (mounted) + setState(() { + _currentIndex = val; + }); + } + + @override + Widget build(BuildContext context) { + if (widget.type == NavigationType.sideDrawer) { + return Scaffold( + appBar: AppBar( + title: Text(widget.children[_currentIndex].title), + actions: widget.children[_currentIndex]?.actions, + ), + drawer: Container( + child: Drawer( + child: SingleChildScrollView( + child: SafeArea( + child: Column(children: [ + for (var tab in widget.children) ...[ + ListTile( + selected: _currentIndex == widget.children.indexOf(tab), + leading: Icon(tab.icon), + title: Text(tab.title), + subtitle: + tab?.description != null ? Text(tab.description) : null, + onTap: () => tabSelected(widget.children.indexOf(tab)), + ), + ], + ]), + ), + )), + ), + body: Stack( + children: [ + for (int i = 0; i < widget.children.length; i++) ...[ + Offstage( + offstage: _currentIndex != i, + child: widget.children[i].child, + ), + ], + ], + ), + ); + } + if (widget.type == NavigationType.bottomTabs) { + return Scaffold( + appBar: AppBar( + title: Text(widget.children[_currentIndex].title), + actions: widget.children[_currentIndex]?.actions, + ), + body: Stack( + children: [ + for (int i = 0; i < widget.children.length; i++) ...[ + Offstage( + offstage: _currentIndex != i, + child: widget.children[i].child, + ), + ], + ], + ), + bottomNavigationBar: BottomNavigationBar( + currentIndex: _currentIndex, + onTap: tabSelected, + items: [ + for (var tab in widget.children) ...[ + BottomNavigationBarItem( + icon: Icon(tab.icon), + title: Text(tab.title), + ), + ], + ], + ), + ); + } + + if (widget.type == NavigationType.pages) { + return DefaultTabController( + initialIndex: _currentIndex, + length: widget.children.length, + child: Scaffold( + appBar: AppBar( + title: Text(widget.children[_currentIndex].title), + actions: widget.children[_currentIndex]?.actions, + bottom: TabBar( + onTap: tabSelected, + tabs: [ + for (var tab in widget.children) ...[tab.tab], + ], + ), + ), + body: Stack( + children: [ + for (int i = 0; i < widget.children.length; i++) ...[ + Offstage( + offstage: _currentIndex != i, + child: widget.children[i].child, + ), + ], + ], + ), + ), + ); + } + + return Container(); + } +} + +enum NavigationType { + bottomTabs, + sideDrawer, + pages, +} + +class Screen { + const Screen({ + @required this.title, + @required this.iconData, + @required this.child, + this.description, + this.iosIconData, + this.pageTab, + this.actions, + }); + + final List actions; + final Widget child; + final String title, description; + final IconData iconData, iosIconData; + final Tab pageTab; + + IconData get icon { + if (Platform.isIOS || Platform.isMacOS) { + return iosIconData ?? iconData; + } + return iconData; + } + + Tab get tab => pageTab ?? Tab(text: title); +} From cceea063f68e578a8d0bb8f1c718a32e4d9a4d55 Mon Sep 17 00:00:00 2001 From: Rody Davis Date: Thu, 6 Jun 2019 14:52:11 -0400 Subject: [PATCH 14/16] Update navigation.dart --- lib/ui/navigation.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ui/navigation.dart b/lib/ui/navigation.dart index 50b515b1..626ffd36 100644 --- a/lib/ui/navigation.dart +++ b/lib/ui/navigation.dart @@ -1,8 +1,8 @@ -import 'package:boilerplate/locale/index.dart'; import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; import '../data/sharedpref/constants/index.dart'; +import '../locale/index.dart'; import '../routes.dart'; import '../widgets/index.dart'; import 'home/home.dart'; From c1cebc2259648513237cc5847a759e699bae9c9c Mon Sep 17 00:00:00 2001 From: Rody Davis Date: Thu, 6 Jun 2019 14:52:26 -0400 Subject: [PATCH 15/16] Updating format --- lib/constants/dimens.dart | 2 +- lib/data/local/datasources/post/post_datasource.dart | 2 +- lib/data/network/apis/posts/post_api.dart | 3 +-- lib/data/network/constants/endpoints.dart | 2 +- lib/data/network/dio_client.dart | 3 +-- lib/data/network/exceptions/network_exceptions.dart | 5 +++-- lib/data/network/rest_client.dart | 10 ++++++---- lib/data/repository.dart | 4 +--- lib/data/sharedpref/constants/preferences.dart | 2 +- lib/models/post/post.dart | 2 -- lib/stores/error/error_store.dart | 3 +-- lib/stores/form/form_store.dart | 4 +++- lib/stores/post/post_store.dart | 3 +-- lib/ui/login/login.dart | 8 ++++---- lib/utils/dio/dio_error_util.dart | 6 +++--- lib/widgets/progress_indicator_widget.dart | 3 +-- lib/widgets/textfield_widget.dart | 1 - test/widget_test.dart | 1 - 18 files changed, 29 insertions(+), 35 deletions(-) diff --git a/lib/constants/dimens.dart b/lib/constants/dimens.dart index 20b774d9..68cfa6c0 100644 --- a/lib/constants/dimens.dart +++ b/lib/constants/dimens.dart @@ -4,4 +4,4 @@ class Dimens { //for all screens static const double horizontal_padding = 12.0; static const double vertical_padding = 12.0; -} \ No newline at end of file +} diff --git a/lib/data/local/datasources/post/post_datasource.dart b/lib/data/local/datasources/post/post_datasource.dart index 543ea155..3e2e0d5e 100644 --- a/lib/data/local/datasources/post/post_datasource.dart +++ b/lib/data/local/datasources/post/post_datasource.dart @@ -85,4 +85,4 @@ class PostDataSource { return post; }).toList(); } -} \ No newline at end of file +} diff --git a/lib/data/network/apis/posts/post_api.dart b/lib/data/network/apis/posts/post_api.dart index 43d63c90..093974b3 100644 --- a/lib/data/network/apis/posts/post_api.dart +++ b/lib/data/network/apis/posts/post_api.dart @@ -25,14 +25,13 @@ class PostApi { /// Returns list of post in response Future getPosts() { - return _dioClient .get(Endpoints.getPosts) .then((dynamic res) => PostsList.fromJson(res)) .catchError((error) => throw error); } -/// sample api call with default rest client + /// sample api call with default rest client // Future getPosts() { // // return _restClient diff --git a/lib/data/network/constants/endpoints.dart b/lib/data/network/constants/endpoints.dart index 25fe2584..e260482b 100644 --- a/lib/data/network/constants/endpoints.dart +++ b/lib/data/network/constants/endpoints.dart @@ -12,4 +12,4 @@ class Endpoints { // booking endpoints static const String getPosts = baseUrl + "/posts"; -} \ No newline at end of file +} diff --git a/lib/data/network/dio_client.dart b/lib/data/network/dio_client.dart index 7b2c3cfd..92cf2ad9 100644 --- a/lib/data/network/dio_client.dart +++ b/lib/data/network/dio_client.dart @@ -24,7 +24,6 @@ final Dio dio = new Dio() })); class DioClient { - // singleton object static final DioClient _singleton = DioClient._(); @@ -37,7 +36,7 @@ class DioClient { // Singleton accessor static DioClient get instance => DioClient(); - + // Get:----------------------------------------------------------------------- Future get(String uri) async { try { diff --git a/lib/data/network/exceptions/network_exceptions.dart b/lib/data/network/exceptions/network_exceptions.dart index f0ba1f8d..5400292e 100644 --- a/lib/data/network/exceptions/network_exceptions.dart +++ b/lib/data/network/exceptions/network_exceptions.dart @@ -5,5 +5,6 @@ class NetworkException implements Exception { } class AuthException extends NetworkException { - AuthException({message, statusCode}) : super(message: message, statusCode: statusCode); -} \ No newline at end of file + AuthException({message, statusCode}) + : super(message: message, statusCode: statusCode); +} diff --git a/lib/data/network/rest_client.dart b/lib/data/network/rest_client.dart index ebfcc559..01549bb5 100644 --- a/lib/data/network/rest_client.dart +++ b/lib/data/network/rest_client.dart @@ -14,10 +14,10 @@ class RestClient { // factory method to return the same object each time its needed factory RestClient() => _singleton; - + // Singleton accessor static RestClient get instance => RestClient(); - + // instantiate json decoder for json serialization final JsonDecoder _decoder = JsonDecoder(); @@ -28,7 +28,8 @@ class RestClient { final int statusCode = response.statusCode; if (statusCode < 200 || statusCode > 400 || json == null) { - throw NetworkException(message:"Error fetching data from server", statusCode: statusCode); + throw NetworkException( + message: "Error fetching data from server", statusCode: statusCode); } print(res); @@ -45,7 +46,8 @@ class RestClient { final int statusCode = response.statusCode; if (statusCode < 200 || statusCode > 400 || json == null) { - throw NetworkException(message:"Error fetching data from server", statusCode: statusCode); + throw NetworkException( + message: "Error fetching data from server", statusCode: statusCode); } return _decoder.convert(res); }); diff --git a/lib/data/repository.dart b/lib/data/repository.dart index 9409c66d..7906a223 100644 --- a/lib/data/repository.dart +++ b/lib/data/repository.dart @@ -39,14 +39,12 @@ class Repository { .catchError((error) => throw error); Future> findPostById(int id) { - //creating filter List filters = List(); //check to see if dataLogsType is not null if (id != null) { - Filter dataLogTypeFilter = - Filter.equal(DBConstants.FIELD_ID, id); + Filter dataLogTypeFilter = Filter.equal(DBConstants.FIELD_ID, id); filters.add(dataLogTypeFilter); } diff --git a/lib/data/sharedpref/constants/preferences.dart b/lib/data/sharedpref/constants/preferences.dart index 7eaaf94b..8d16edcf 100644 --- a/lib/data/sharedpref/constants/preferences.dart +++ b/lib/data/sharedpref/constants/preferences.dart @@ -3,4 +3,4 @@ class Preferences { static const String is_logged_in = "isLoggedIn"; static const String auth_token = "authToken"; -} \ No newline at end of file +} diff --git a/lib/models/post/post.dart b/lib/models/post/post.dart index aa9acd2b..317c9e5b 100644 --- a/lib/models/post/post.dart +++ b/lib/models/post/post.dart @@ -1,4 +1,3 @@ - class Post { int userId; int id; @@ -25,5 +24,4 @@ class Post { "title": title, "body": body, }; - } diff --git a/lib/stores/error/error_store.dart b/lib/stores/error/error_store.dart index de18af79..b7c81d38 100644 --- a/lib/stores/error/error_store.dart +++ b/lib/stores/error/error_store.dart @@ -5,11 +5,10 @@ part 'error_store.g.dart'; class ErrorStore = _ErrorStore with _$ErrorStore; abstract class _ErrorStore implements Store { - // store variables:----------------------------------------------------------- @observable String errorMessage; @observable bool showError = false; -} \ No newline at end of file +} diff --git a/lib/stores/form/form_store.dart b/lib/stores/form/form_store.dart index 1a73c1bb..5498a0cb 100644 --- a/lib/stores/form/form_store.dart +++ b/lib/stores/form/form_store.dart @@ -50,7 +50,9 @@ abstract class _FormStore implements Store { @computed bool get canLogin => - !formErrorStore.hasErrorsInLogin && userEmail.isNotEmpty && password.isNotEmpty; + !formErrorStore.hasErrorsInLogin && + userEmail.isNotEmpty && + password.isNotEmpty; @computed bool get canRegister => diff --git a/lib/stores/post/post_store.dart b/lib/stores/post/post_store.dart index cd01acf8..d59d4aa3 100644 --- a/lib/stores/post/post_store.dart +++ b/lib/stores/post/post_store.dart @@ -9,7 +9,6 @@ part 'post_store.g.dart'; class PostStore = _PostStore with _$PostStore; abstract class _PostStore implements Store { - // store for handling errors final ErrorStore errorStore = ErrorStore(); @@ -41,4 +40,4 @@ abstract class _PostStore implements Store { print(e); }); } -} \ No newline at end of file +} diff --git a/lib/ui/login/login.dart b/lib/ui/login/login.dart index 237df7c0..60ef54d9 100644 --- a/lib/ui/login/login.dart +++ b/lib/ui/login/login.dart @@ -212,7 +212,8 @@ class _LoginScreenState extends State { if (_store.canLogin) { _store.login(); } else { - showErrorMessage(context, AppLocalizations.of(context).login_validation_error); + showErrorMessage( + context, AppLocalizations.of(context).login_validation_error); } }, ); @@ -220,13 +221,12 @@ class _LoginScreenState extends State { // General Methods:----------------------------------------------------------- showErrorMessage(BuildContext context, String message) { - if(message != null) { + if (message != null) { FlushbarHelper.createError( message: message, title: 'Error', duration: Duration(seconds: 3), - ) - ..show(context); + )..show(context); } return Container(); diff --git a/lib/utils/dio/dio_error_util.dart b/lib/utils/dio/dio_error_util.dart index dbbebc8f..b5e9d816 100644 --- a/lib/utils/dio/dio_error_util.dart +++ b/lib/utils/dio/dio_error_util.dart @@ -14,14 +14,14 @@ class DioErrorUtil { break; case DioErrorType.DEFAULT: errorDescription = - "Connection to API server failed due to internet connection"; + "Connection to API server failed due to internet connection"; break; case DioErrorType.RECEIVE_TIMEOUT: errorDescription = "Receive timeout in connection with API server"; break; case DioErrorType.RESPONSE: errorDescription = - "Received invalid status code: ${error.response.statusCode}"; + "Received invalid status code: ${error.response.statusCode}"; break; case DioErrorType.SEND_TIMEOUT: errorDescription = "Send timeout in connection with API server"; @@ -32,4 +32,4 @@ class DioErrorUtil { } return errorDescription; } -} \ No newline at end of file +} diff --git a/lib/widgets/progress_indicator_widget.dart b/lib/widgets/progress_indicator_widget.dart index 0bc91600..420d4ecc 100644 --- a/lib/widgets/progress_indicator_widget.dart +++ b/lib/widgets/progress_indicator_widget.dart @@ -27,8 +27,7 @@ class CustomProgressIndicatorWidget extends StatelessWidget { ), ), ), - decoration: BoxDecoration( - color: Color.fromARGB(100, 105, 105, 105)), + decoration: BoxDecoration(color: Color.fromARGB(100, 105, 105, 105)), ), ); } diff --git a/lib/widgets/textfield_widget.dart b/lib/widgets/textfield_widget.dart index 4962d3e5..8a8923d1 100644 --- a/lib/widgets/textfield_widget.dart +++ b/lib/widgets/textfield_widget.dart @@ -58,5 +58,4 @@ class TextFieldWidget extends StatelessWidget { ), ); } - } diff --git a/test/widget_test.dart b/test/widget_test.dart index a48deac4..40aa4800 100644 --- a/test/widget_test.dart +++ b/test/widget_test.dart @@ -9,7 +9,6 @@ import 'package:boilerplate/main.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; - void main() { testWidgets('Counter increments smoke test', (WidgetTester tester) async { // Build our app and trigger a frame. From 7560dabc01c56821c38f12d8504aba94269fb7fd Mon Sep 17 00:00:00 2001 From: Rody Davis Date: Thu, 6 Jun 2019 15:16:12 -0400 Subject: [PATCH 16/16] Building Widgets Based on Size - Fixing Login Screen - Adding Master Detail to List View --- lib/constants/dimens.dart | 3 + lib/ui/home/home.dart | 134 ++++++++++++++++++++++++++++++++------ lib/ui/login/login.dart | 43 ++++++------ lib/ui/splash/splash.dart | 2 +- 4 files changed, 140 insertions(+), 42 deletions(-) diff --git a/lib/constants/dimens.dart b/lib/constants/dimens.dart index 68cfa6c0..d509316e 100644 --- a/lib/constants/dimens.dart +++ b/lib/constants/dimens.dart @@ -4,4 +4,7 @@ class Dimens { //for all screens static const double horizontal_padding = 12.0; static const double vertical_padding = 12.0; + + static const double tablet_breakpoint = 720.0; + static const double tablet_list_width = 400.0; } diff --git a/lib/ui/home/home.dart b/lib/ui/home/home.dart index bd4702e1..cfbd2131 100644 --- a/lib/ui/home/home.dart +++ b/lib/ui/home/home.dart @@ -1,3 +1,5 @@ +import 'package:boilerplate/constants/index.dart'; +import 'package:boilerplate/models/post/index.dart'; import 'package:flushbar/flushbar_helper.dart'; import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; @@ -26,30 +28,86 @@ class _HomeScreenState extends State { _store.getPosts(); } + int _selectedIndex = 0; @override Widget build(BuildContext context) { - return Stack( - children: [ - Observer( - builder: (context) { - return _store.loading - ? CustomProgressIndicatorWidget() - : Material(child: _buildListView()); - }, - ), - Observer( - name: 'error', - builder: (context) { - return _store.success - ? Container() - : showErrorMessage(context, _store.errorStore.errorMessage); - }, - ) - ], + return LayoutBuilder( + builder: (context, dimens) { + if (MediaQuery.of(context).orientation == Orientation.landscape && + dimens.maxWidth >= Dimens.tablet_breakpoint) { + return Row( + children: [ + Container( + width: Dimens.tablet_list_width, + child: Stack( + children: [ + Observer( + builder: (context) { + return _store.loading + ? CustomProgressIndicatorWidget() + : Material( + child: _buildListView((val) { + if (mounted) + setState(() { + _selectedIndex = val; + }); + }, true)); + }, + ), + Observer( + name: 'error', + builder: (context) { + return _store.success + ? Container() + : showErrorMessage( + context, _store.errorStore.errorMessage); + }, + ) + ], + ), + ), + Expanded( + child: PostDetailsScreen( + post: _store.postsList.posts[_selectedIndex], + showAppBar: false, + ), + ), + ], + ); + } + return Stack( + children: [ + Observer( + builder: (context) { + return _store.loading + ? CustomProgressIndicatorWidget() + : Material( + child: _buildListView((val) { + Navigator.of(context).push( + MaterialPageRoute( + builder: (_) => PostDetailsScreen( + post: _store.postsList.posts[_selectedIndex], + ), + ), + ); + }, false)); + }, + ), + Observer( + name: 'error', + builder: (context) { + return _store.success + ? Container() + : showErrorMessage(context, _store.errorStore.errorMessage); + }, + ) + ], + ); + }, ); } - Widget _buildListView() { + Widget _buildListView(ValueChanged selected, bool tablet) { return _store.postsList != null ? ListView.separated( itemCount: _store.postsList.posts.length, @@ -58,6 +116,7 @@ class _HomeScreenState extends State { }, itemBuilder: (context, position) { return ListTile( + selected: tablet ? _selectedIndex == position : false, leading: Icon(Icons.cloud_circle), title: Text( '${_store.postsList.posts[position].title}', @@ -72,6 +131,7 @@ class _HomeScreenState extends State { overflow: TextOverflow.ellipsis, softWrap: false, ), + onTap: () => selected(position), ); }, ) @@ -93,3 +153,39 @@ class _HomeScreenState extends State { return Container(); } } + +class PostDetailsScreen extends StatelessWidget { + const PostDetailsScreen({ + @required this.post, + this.showAppBar = true, + }); + final Post post; + final bool showAppBar; + @override + Widget build(BuildContext context) { + final _textTheme = Theme.of(context).textTheme; + return Scaffold( + appBar: showAppBar + ? AppBar( + title: Text('Details'), + ) + : null, + body: ListView( + children: [ + ListTile( + title: Text( + post.title, + style: _textTheme.title, + ), + ), + ListTile( + title: Text( + post.body, + style: _textTheme.subtitle, + ), + ), + ], + ), + ); + } +} diff --git a/lib/ui/login/login.dart b/lib/ui/login/login.dart index 60ef54d9..cd24cae1 100644 --- a/lib/ui/login/login.dart +++ b/lib/ui/login/login.dart @@ -1,3 +1,4 @@ +import 'package:boilerplate/constants/index.dart'; import 'package:flushbar/flushbar_helper.dart'; import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; @@ -70,29 +71,27 @@ class _LoginScreenState extends State { return Material( child: Stack( children: [ - OrientationBuilder( - builder: (context, orientation) { - //variable to hold widget - var child; - + LayoutBuilder( + builder: (context, dimens) { //check to see whether device is in landscape or portrait - //load widgets based on device orientation - orientation == Orientation.landscape - ? child = Row( - children: [ - Expanded( - flex: 1, - child: _buildLeftSide(), - ), - Expanded( - flex: 1, - child: _buildRightSide(), - ), - ], - ) - : child = Center(child: _buildRightSide()); - - return child; + //load widgets based on device orientation and max width + if (MediaQuery.of(context).orientation == Orientation.landscape || + dimens.maxWidth >= Dimens.tablet_breakpoint) { + return Row( + children: [ + Expanded( + flex: 1, + child: _buildLeftSide(), + ), + Expanded( + flex: 1, + child: _buildRightSide(), + ), + ], + ); + } + + return Center(child: _buildRightSide()); }, ), Observer( diff --git a/lib/ui/splash/splash.dart b/lib/ui/splash/splash.dart index 28bc00c5..f9f035b1 100644 --- a/lib/ui/splash/splash.dart +++ b/lib/ui/splash/splash.dart @@ -36,7 +36,7 @@ class _SplashScreenState extends State { navigate() async { SharedPreferences preferences = await SharedPreferences.getInstance(); - if (autoLogin && preferences.getBool(Preferences.is_logged_in) ?? false) { + if (autoLogin && (preferences?.getBool(Preferences.is_logged_in) ?? false)) { Navigator.of(context).pushReplacementNamed(Routes.home); } else { Navigator.of(context).pushReplacementNamed(Routes.login);