| @@ -0,0 +1,46 @@ | |||
| PODS: | |||
| - Flutter (1.0.0) | |||
| - path_provider_linux (0.0.1): | |||
| - Flutter | |||
| - shared_preferences (0.0.1): | |||
| - Flutter | |||
| - shared_preferences_linux (0.0.1): | |||
| - Flutter | |||
| - shared_preferences_macos (0.0.1): | |||
| - Flutter | |||
| - shared_preferences_web (0.0.1): | |||
| - Flutter | |||
| DEPENDENCIES: | |||
| - Flutter (from `Flutter`) | |||
| - path_provider_linux (from `.symlinks/plugins/path_provider_linux/ios`) | |||
| - shared_preferences (from `.symlinks/plugins/shared_preferences/ios`) | |||
| - shared_preferences_linux (from `.symlinks/plugins/shared_preferences_linux/ios`) | |||
| - shared_preferences_macos (from `.symlinks/plugins/shared_preferences_macos/ios`) | |||
| - shared_preferences_web (from `.symlinks/plugins/shared_preferences_web/ios`) | |||
| EXTERNAL SOURCES: | |||
| Flutter: | |||
| :path: Flutter | |||
| path_provider_linux: | |||
| :path: ".symlinks/plugins/path_provider_linux/ios" | |||
| shared_preferences: | |||
| :path: ".symlinks/plugins/shared_preferences/ios" | |||
| shared_preferences_linux: | |||
| :path: ".symlinks/plugins/shared_preferences_linux/ios" | |||
| shared_preferences_macos: | |||
| :path: ".symlinks/plugins/shared_preferences_macos/ios" | |||
| shared_preferences_web: | |||
| :path: ".symlinks/plugins/shared_preferences_web/ios" | |||
| SPEC CHECKSUMS: | |||
| Flutter: 0e3d915762c693b495b44d77113d4970485de6ec | |||
| path_provider_linux: 4d630dc393e1f20364f3e3b4a2ff41d9674a84e4 | |||
| shared_preferences: af6bfa751691cdc24be3045c43ec037377ada40d | |||
| shared_preferences_linux: afefbfe8d921e207f01ede8b60373d9e3b566b78 | |||
| shared_preferences_macos: f3f29b71ccbb56bf40c9dd6396c9acf15e214087 | |||
| shared_preferences_web: 141cce0c3ed1a1c5bf2a0e44f52d31eeb66e5ea9 | |||
| PODFILE CHECKSUM: c34e2287a9ccaa606aeceab922830efb9a6ff69a | |||
| COCOAPODS: 1.9.3 | |||
| @@ -10,6 +10,7 @@ | |||
| 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; | |||
| 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; | |||
| 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; | |||
| 97280A7711C46F9F9C83E537 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4D01659C26BFB65615058BF0 /* Pods_Runner.framework */; }; | |||
| 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; | |||
| 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; | |||
| 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; | |||
| @@ -29,12 +30,16 @@ | |||
| /* End PBXCopyFilesBuildPhase section */ | |||
| /* Begin PBXFileReference section */ | |||
| 0BC0F43034414FAB24B626F3 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; }; | |||
| 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; }; | |||
| 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; }; | |||
| 1BA1D19EF353DFCE335E0C1F /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; }; | |||
| 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; }; | |||
| 4D01659C26BFB65615058BF0 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; | |||
| 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; }; | |||
| 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; }; | |||
| 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; }; | |||
| 95FEBA268448B96856A798E7 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; }; | |||
| 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; }; | |||
| 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; }; | |||
| 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; | |||
| @@ -49,12 +54,32 @@ | |||
| isa = PBXFrameworksBuildPhase; | |||
| buildActionMask = 2147483647; | |||
| files = ( | |||
| 97280A7711C46F9F9C83E537 /* Pods_Runner.framework in Frameworks */, | |||
| ); | |||
| runOnlyForDeploymentPostprocessing = 0; | |||
| }; | |||
| /* End PBXFrameworksBuildPhase section */ | |||
| /* Begin PBXGroup section */ | |||
| 2C5DAD4B82BC146A052F8839 /* Pods */ = { | |||
| isa = PBXGroup; | |||
| children = ( | |||
| 0BC0F43034414FAB24B626F3 /* Pods-Runner.debug.xcconfig */, | |||
| 1BA1D19EF353DFCE335E0C1F /* Pods-Runner.release.xcconfig */, | |||
| 95FEBA268448B96856A798E7 /* Pods-Runner.profile.xcconfig */, | |||
| ); | |||
| name = Pods; | |||
| path = Pods; | |||
| sourceTree = "<group>"; | |||
| }; | |||
| 9220643F6C3D895C737199D7 /* Frameworks */ = { | |||
| isa = PBXGroup; | |||
| children = ( | |||
| 4D01659C26BFB65615058BF0 /* Pods_Runner.framework */, | |||
| ); | |||
| name = Frameworks; | |||
| sourceTree = "<group>"; | |||
| }; | |||
| 9740EEB11CF90186004384FC /* Flutter */ = { | |||
| isa = PBXGroup; | |||
| children = ( | |||
| @@ -72,6 +97,8 @@ | |||
| 9740EEB11CF90186004384FC /* Flutter */, | |||
| 97C146F01CF9000F007C117D /* Runner */, | |||
| 97C146EF1CF9000F007C117D /* Products */, | |||
| 2C5DAD4B82BC146A052F8839 /* Pods */, | |||
| 9220643F6C3D895C737199D7 /* Frameworks */, | |||
| ); | |||
| sourceTree = "<group>"; | |||
| }; | |||
| @@ -113,12 +140,14 @@ | |||
| isa = PBXNativeTarget; | |||
| buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; | |||
| buildPhases = ( | |||
| F9CCF48907EF75C488FA6995 /* [CP] Check Pods Manifest.lock */, | |||
| 9740EEB61CF901F6004384FC /* Run Script */, | |||
| 97C146EA1CF9000F007C117D /* Sources */, | |||
| 97C146EB1CF9000F007C117D /* Frameworks */, | |||
| 97C146EC1CF9000F007C117D /* Resources */, | |||
| 9705A1C41CF9048500538489 /* Embed Frameworks */, | |||
| 3B06AD1E1E4923F5004D2608 /* Thin Binary */, | |||
| 8A5D95728F54511932EFB110 /* [CP] Embed Pods Frameworks */, | |||
| ); | |||
| buildRules = ( | |||
| ); | |||
| @@ -191,6 +220,26 @@ | |||
| shellPath = /bin/sh; | |||
| shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; | |||
| }; | |||
| 8A5D95728F54511932EFB110 /* [CP] Embed Pods Frameworks */ = { | |||
| isa = PBXShellScriptBuildPhase; | |||
| buildActionMask = 2147483647; | |||
| files = ( | |||
| ); | |||
| inputPaths = ( | |||
| "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", | |||
| "${PODS_ROOT}/../Flutter/Flutter.framework", | |||
| "${BUILT_PRODUCTS_DIR}/shared_preferences/shared_preferences.framework", | |||
| ); | |||
| name = "[CP] Embed Pods Frameworks"; | |||
| outputPaths = ( | |||
| "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework", | |||
| "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/shared_preferences.framework", | |||
| ); | |||
| runOnlyForDeploymentPostprocessing = 0; | |||
| shellPath = /bin/sh; | |||
| shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; | |||
| showEnvVarsInLog = 0; | |||
| }; | |||
| 9740EEB61CF901F6004384FC /* Run Script */ = { | |||
| isa = PBXShellScriptBuildPhase; | |||
| buildActionMask = 2147483647; | |||
| @@ -205,6 +254,28 @@ | |||
| shellPath = /bin/sh; | |||
| shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; | |||
| }; | |||
| F9CCF48907EF75C488FA6995 /* [CP] Check Pods Manifest.lock */ = { | |||
| isa = PBXShellScriptBuildPhase; | |||
| buildActionMask = 2147483647; | |||
| files = ( | |||
| ); | |||
| inputFileListPaths = ( | |||
| ); | |||
| inputPaths = ( | |||
| "${PODS_PODFILE_DIR_PATH}/Podfile.lock", | |||
| "${PODS_ROOT}/Manifest.lock", | |||
| ); | |||
| name = "[CP] Check Pods Manifest.lock"; | |||
| outputFileListPaths = ( | |||
| ); | |||
| outputPaths = ( | |||
| "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", | |||
| ); | |||
| runOnlyForDeploymentPostprocessing = 0; | |||
| shellPath = /bin/sh; | |||
| shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; | |||
| showEnvVarsInLog = 0; | |||
| }; | |||
| /* End PBXShellScriptBuildPhase section */ | |||
| /* Begin PBXSourcesBuildPhase section */ | |||
| @@ -4,4 +4,7 @@ | |||
| <FileRef | |||
| location = "group:Runner.xcodeproj"> | |||
| </FileRef> | |||
| <FileRef | |||
| location = "group:Pods/Pods.xcodeproj"> | |||
| </FileRef> | |||
| </Workspace> | |||
| @@ -0,0 +1,78 @@ | |||
| import 'package:authentication_repository/authentication_repository.dart'; | |||
| import 'package:farm_tpf/presentation/screens/home/view/home_page.dart'; | |||
| import 'package:farm_tpf/presentation/screens/login/view/login_page.dart'; | |||
| import 'package:farm_tpf/presentation/screens/splash/view/splash_page.dart'; | |||
| import 'package:flutter/material.dart'; | |||
| import 'package:flutter_bloc/flutter_bloc.dart'; | |||
| import 'package:user_repository/user_repository.dart'; | |||
| import 'authentication/bloc/authentication_bloc.dart'; | |||
| class App extends StatelessWidget { | |||
| const App({ | |||
| Key key, | |||
| @required this.authenticationRepository, | |||
| @required this.userRepository, | |||
| }) : assert(authenticationRepository != null), | |||
| assert(userRepository != null), | |||
| super(key: key); | |||
| final AuthenticationRepository authenticationRepository; | |||
| final UserRepository userRepository; | |||
| @override | |||
| Widget build(BuildContext context) { | |||
| return RepositoryProvider.value( | |||
| value: authenticationRepository, | |||
| child: BlocProvider( | |||
| create: (_) => AuthenticationBloc( | |||
| authenticationRepository: authenticationRepository, | |||
| userRepository: userRepository, | |||
| ), | |||
| child: AppView(), | |||
| ), | |||
| ); | |||
| } | |||
| } | |||
| class AppView extends StatefulWidget { | |||
| @override | |||
| _AppViewState createState() => _AppViewState(); | |||
| } | |||
| class _AppViewState extends State<AppView> { | |||
| final _navigatorKey = GlobalKey<NavigatorState>(); | |||
| NavigatorState get _navigator => _navigatorKey.currentState; | |||
| @override | |||
| Widget build(BuildContext context) { | |||
| return MaterialApp( | |||
| navigatorKey: _navigatorKey, | |||
| builder: (context, child) { | |||
| return BlocListener<AuthenticationBloc, AuthenticationState>( | |||
| listener: (context, state) { | |||
| switch (state.status) { | |||
| case AuthenticationStatus.authenticated: | |||
| _navigator.pushAndRemoveUntil<void>( | |||
| HomePage.route(), | |||
| (route) => false, | |||
| ); | |||
| break; | |||
| case AuthenticationStatus.unauthenticated: | |||
| _navigator.pushAndRemoveUntil<void>( | |||
| LoginPage.route(), | |||
| (route) => false, | |||
| ); | |||
| break; | |||
| default: | |||
| break; | |||
| } | |||
| }, | |||
| child: child, | |||
| ); | |||
| }, | |||
| onGenerateRoute: (_) => SplashPage.route(), | |||
| ); | |||
| } | |||
| } | |||
| @@ -1,65 +0,0 @@ | |||
| import 'package:farm_tpf/app/themes.dart'; | |||
| import 'package:farm_tpf/utils/const_string.dart'; | |||
| import 'package:flutter/material.dart'; | |||
| class MyApp extends StatelessWidget { | |||
| // This widget is the root of your application. | |||
| @override | |||
| Widget build(BuildContext context) { | |||
| return MaterialApp( | |||
| title: 'Flutter Demo', | |||
| theme: AppTheme.of(context, listen: true).currentTheme, | |||
| home: MyHomePage(title: 'Flutter Demo Home Page'), | |||
| ); | |||
| } | |||
| } | |||
| class MyHomePage extends StatefulWidget { | |||
| MyHomePage({Key key, this.title}) : super(key: key); | |||
| final String title; | |||
| @override | |||
| _MyHomePageState createState() => _MyHomePageState(); | |||
| } | |||
| class _MyHomePageState extends State<MyHomePage> { | |||
| int _counter = 0; | |||
| AppTheme _theme; | |||
| @override | |||
| void didChangeDependencies() { | |||
| if (_theme == null) { | |||
| _theme = AppTheme.of(context); | |||
| } | |||
| super.didChangeDependencies(); | |||
| } | |||
| 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 | |||
| Widget build(BuildContext context) { | |||
| return Scaffold( | |||
| appBar: AppBar( | |||
| title: Text(widget.title), | |||
| actions: [ | |||
| IconButton( | |||
| icon: Icon(Icons.flip, color: Colors.white), | |||
| onPressed: _theme?.switchTheme, | |||
| ), | |||
| ], | |||
| ), | |||
| body: Center(child: Text(app_name))); | |||
| } | |||
| } | |||
| @@ -1,42 +0,0 @@ | |||
| import 'package:farm_tpf/utils/const_color.dart'; | |||
| import 'package:flutter/material.dart'; | |||
| import 'package:provider/provider.dart'; | |||
| enum AppThemeStyle { light, dark } | |||
| final Map<AppThemeStyle, ThemeData> _themes = { | |||
| AppThemeStyle.light: ThemeData( | |||
| primaryColor: Colors.green, | |||
| accentColor: Colors.green, | |||
| brightness: Brightness.light, | |||
| ), | |||
| AppThemeStyle.dark: ThemeData( | |||
| primaryColor: Colors.blue, | |||
| accentColor: Colors.blue, | |||
| brightness: Brightness.dark, | |||
| ), | |||
| }; | |||
| class AppTheme extends ChangeNotifier { | |||
| static AppTheme of(BuildContext context, {bool listen = false}) => | |||
| Provider.of<AppTheme>(context, listen: listen); | |||
| AppThemeStyle _themeKey = AppThemeStyle.light; | |||
| ThemeData get currentTheme => _themes[_themeKey]; | |||
| AppThemeStyle get currentThemeKey => _themeKey; | |||
| void setTheme(AppThemeStyle themeKey) { | |||
| _themeKey = themeKey; | |||
| notifyListeners(); | |||
| } | |||
| void switchTheme() { | |||
| if (_themeKey == AppThemeStyle.dark) { | |||
| _themeKey = AppThemeStyle.light; | |||
| } else { | |||
| _themeKey = AppThemeStyle.dark; | |||
| } | |||
| notifyListeners(); | |||
| } | |||
| } | |||
| @@ -0,0 +1 @@ | |||
| export 'bloc/authentication_bloc.dart'; | |||
| @@ -0,0 +1,73 @@ | |||
| import 'dart:async'; | |||
| import 'package:authentication_repository/authentication_repository.dart'; | |||
| import 'package:bloc/bloc.dart'; | |||
| import 'package:equatable/equatable.dart'; | |||
| import 'package:meta/meta.dart'; | |||
| import 'package:user_repository/user_repository.dart'; | |||
| part 'authentication_event.dart'; | |||
| part 'authentication_state.dart'; | |||
| class AuthenticationBloc | |||
| extends Bloc<AuthenticationEvent, AuthenticationState> { | |||
| AuthenticationBloc({ | |||
| @required AuthenticationRepository authenticationRepository, | |||
| @required UserRepository userRepository, | |||
| }) : assert(authenticationRepository != null), | |||
| assert(userRepository != null), | |||
| _authenticationRepository = authenticationRepository, | |||
| _userRepository = userRepository, | |||
| super(const AuthenticationState.unknown()) { | |||
| _authenticationStatusSubscription = _authenticationRepository.status.listen( | |||
| (status) => add(AuthenticationStatusChanged(status)), | |||
| ); | |||
| } | |||
| final AuthenticationRepository _authenticationRepository; | |||
| final UserRepository _userRepository; | |||
| StreamSubscription<AuthenticationStatus> _authenticationStatusSubscription; | |||
| @override | |||
| Stream<AuthenticationState> mapEventToState( | |||
| AuthenticationEvent event, | |||
| ) async* { | |||
| if (event is AuthenticationStatusChanged) { | |||
| yield await _mapAuthenticationStatusChangedToState(event); | |||
| } else if (event is AuthenticationLogoutRequested) { | |||
| _authenticationRepository.logOut(); | |||
| } | |||
| } | |||
| @override | |||
| Future<void> close() { | |||
| _authenticationStatusSubscription?.cancel(); | |||
| _authenticationRepository.dispose(); | |||
| return super.close(); | |||
| } | |||
| Future<AuthenticationState> _mapAuthenticationStatusChangedToState( | |||
| AuthenticationStatusChanged event, | |||
| ) async { | |||
| switch (event.status) { | |||
| case AuthenticationStatus.unauthenticated: | |||
| return const AuthenticationState.unauthenticated(); | |||
| case AuthenticationStatus.authenticated: | |||
| final user = await _tryGetUser(); | |||
| return user != null | |||
| ? AuthenticationState.authenticated(user) | |||
| : const AuthenticationState.unauthenticated(); | |||
| default: | |||
| return const AuthenticationState.unknown(); | |||
| } | |||
| } | |||
| Future<User> _tryGetUser() async { | |||
| try { | |||
| final user = await _userRepository.getUser(); | |||
| return user; | |||
| } on Exception { | |||
| return null; | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,19 @@ | |||
| part of 'authentication_bloc.dart'; | |||
| abstract class AuthenticationEvent extends Equatable { | |||
| const AuthenticationEvent(); | |||
| @override | |||
| List<Object> get props => []; | |||
| } | |||
| class AuthenticationStatusChanged extends AuthenticationEvent { | |||
| const AuthenticationStatusChanged(this.status); | |||
| final AuthenticationStatus status; | |||
| @override | |||
| List<Object> get props => [status]; | |||
| } | |||
| class AuthenticationLogoutRequested extends AuthenticationEvent {} | |||
| @@ -0,0 +1,22 @@ | |||
| part of 'authentication_bloc.dart'; | |||
| class AuthenticationState extends Equatable { | |||
| const AuthenticationState._({ | |||
| this.status = AuthenticationStatus.unknown, | |||
| this.user, | |||
| }); | |||
| const AuthenticationState.unknown() : this._(); | |||
| const AuthenticationState.authenticated(User user) | |||
| : this._(status: AuthenticationStatus.authenticated, user: user); | |||
| const AuthenticationState.unauthenticated() | |||
| : this._(status: AuthenticationStatus.unauthenticated); | |||
| final AuthenticationStatus status; | |||
| final User user; | |||
| @override | |||
| List<Object> get props => [status, user]; | |||
| } | |||
| @@ -0,0 +1,44 @@ | |||
| import 'dart:developer'; | |||
| import 'package:dio/dio.dart'; | |||
| import 'package:farm_tpf/utils/pref.dart'; | |||
| class DioProvider { | |||
| static Dio instance() { | |||
| final dio = Dio(); | |||
| // dio.interceptors.add(AuthInterceptor()); | |||
| dio.interceptors.add(HttpLogInterceptor()); | |||
| return dio; | |||
| } | |||
| } | |||
| class HttpLogInterceptor extends InterceptorsWrapper { | |||
| var pref = LocalPref(); | |||
| @override | |||
| Future onRequest(RequestOptions options) async { | |||
| var token = await pref.getString(PrefKey.token_key); | |||
| options.headers["Authorization"] = "Bearer $token"; | |||
| options.receiveTimeout = 20000; | |||
| // log("onRequest: ${options.uri}\n" | |||
| // "data=${options.data}\n" | |||
| // "method=${options.method}\n" | |||
| // "headers=${options.headers}\n" | |||
| // "queryParameters=${options.queryParameters}"); | |||
| return options; | |||
| } | |||
| @override | |||
| Future onResponse(Response response) { | |||
| // log("onResponse: $response"); | |||
| return super.onResponse(response); | |||
| } | |||
| @override | |||
| Future onError(DioError err) { | |||
| // log("onError: $err\n" | |||
| // "Response: ${err.response}"); | |||
| return super.onError(err); | |||
| } | |||
| } | |||
| @@ -0,0 +1,14 @@ | |||
| import 'package:dio/dio.dart'; | |||
| import 'package:farm_tpf/models/user.dart'; | |||
| import 'package:farm_tpf/models/user_request.dart'; | |||
| import 'package:retrofit/retrofit.dart'; | |||
| part 'rest_client.g.dart'; | |||
| @RestApi(baseUrl: "https://aquaman.aztrace.vn") | |||
| abstract class RestClient { | |||
| factory RestClient(Dio dio, {String baseUrl}) = _RestClient; | |||
| @POST("/api/authenticate") | |||
| Future<User> login(@Body() UserRequest userRequest); | |||
| } | |||
| @@ -0,0 +1,38 @@ | |||
| // GENERATED CODE - DO NOT MODIFY BY HAND | |||
| part of 'rest_client.dart'; | |||
| // ************************************************************************** | |||
| // RetrofitGenerator | |||
| // ************************************************************************** | |||
| class _RestClient implements RestClient { | |||
| _RestClient(this._dio, {this.baseUrl}) { | |||
| ArgumentError.checkNotNull(_dio, '_dio'); | |||
| this.baseUrl ??= 'https://aquaman.aztrace.vn'; | |||
| } | |||
| final Dio _dio; | |||
| String baseUrl; | |||
| @override | |||
| login(userRequest) async { | |||
| ArgumentError.checkNotNull(userRequest, 'userRequest'); | |||
| const _extra = <String, dynamic>{}; | |||
| final queryParameters = <String, dynamic>{}; | |||
| final _data = <String, dynamic>{}; | |||
| _data.addAll(userRequest?.toJson() ?? <String, dynamic>{}); | |||
| final Response<Map<String, dynamic>> _result = await _dio.request( | |||
| '/api/authenticate', | |||
| queryParameters: queryParameters, | |||
| options: RequestOptions( | |||
| method: 'POST', | |||
| headers: <String, dynamic>{}, | |||
| extra: _extra, | |||
| baseUrl: baseUrl), | |||
| data: _data); | |||
| final value = User.fromJson(_result.data); | |||
| return value; | |||
| } | |||
| } | |||
| @@ -0,0 +1,13 @@ | |||
| import 'package:farm_tpf/data/api/dio_provider.dart'; | |||
| import 'package:farm_tpf/data/api/rest_client.dart'; | |||
| import 'package:farm_tpf/models/user.dart'; | |||
| import 'package:farm_tpf/models/user_request.dart'; | |||
| class Repository { | |||
| final dio = DioProvider.instance(); | |||
| Future<User> signInWithCredentials(String username, String password) { | |||
| final client = RestClient(dio); | |||
| return client.login(UserRequest(username: username, password: password)); | |||
| } | |||
| } | |||
| @@ -1,12 +1,11 @@ | |||
| import 'package:farm_tpf/app/themes.dart'; | |||
| import 'package:flutter/material.dart'; | |||
| import 'package:provider/provider.dart'; | |||
| import 'app/my_app.dart'; | |||
| import 'package:authentication_repository/authentication_repository.dart'; | |||
| import 'package:user_repository/user_repository.dart'; | |||
| import 'app.dart'; | |||
| void main() { | |||
| runApp(ChangeNotifierProvider( | |||
| create: (_) => AppTheme(), | |||
| child: MyApp(), | |||
| runApp(App( | |||
| authenticationRepository: AuthenticationRepository(), | |||
| userRepository: UserRepository(), | |||
| )); | |||
| } | |||
| @@ -0,0 +1,13 @@ | |||
| import 'package:json_annotation/json_annotation.dart'; | |||
| part 'user.g.dart'; | |||
| @JsonSerializable() | |||
| class User { | |||
| User(); | |||
| String idToken; | |||
| factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json); | |||
| Map<String, dynamic> toJson() => _$UserToJson(this); | |||
| } | |||
| @@ -0,0 +1,15 @@ | |||
| // GENERATED CODE - DO NOT MODIFY BY HAND | |||
| part of 'user.dart'; | |||
| // ************************************************************************** | |||
| // JsonSerializableGenerator | |||
| // ************************************************************************** | |||
| User _$UserFromJson(Map<String, dynamic> json) { | |||
| return User()..idToken = json['id_token'] as String; | |||
| } | |||
| Map<String, dynamic> _$UserToJson(User instance) => <String, dynamic>{ | |||
| 'id_token': instance.idToken, | |||
| }; | |||
| @@ -0,0 +1,13 @@ | |||
| import 'package:json_annotation/json_annotation.dart'; | |||
| part 'user_request.g.dart'; | |||
| @JsonSerializable() | |||
| class UserRequest { | |||
| final String username; | |||
| final String password; | |||
| UserRequest({this.username, this.password}); | |||
| factory UserRequest.fromJson(Map<String, dynamic> json) => | |||
| _$UserRequestFromJson(json); | |||
| Map<String, dynamic> toJson() => _$UserRequestToJson(this); | |||
| } | |||
| @@ -0,0 +1,20 @@ | |||
| // GENERATED CODE - DO NOT MODIFY BY HAND | |||
| part of 'user_request.dart'; | |||
| // ************************************************************************** | |||
| // JsonSerializableGenerator | |||
| // ************************************************************************** | |||
| UserRequest _$UserRequestFromJson(Map<String, dynamic> json) { | |||
| return UserRequest( | |||
| username: json['username'] as String, | |||
| password: json['password'] as String, | |||
| ); | |||
| } | |||
| Map<String, dynamic> _$UserRequestToJson(UserRequest instance) => | |||
| <String, dynamic>{ | |||
| 'username': instance.username, | |||
| 'password': instance.password, | |||
| }; | |||
| @@ -0,0 +1,50 @@ | |||
| import 'package:flutter/material.dart'; | |||
| class LoadingDialog { | |||
| static void showLoadingDialog(BuildContext context) { | |||
| showDialog( | |||
| context: context, | |||
| barrierDismissible: false, | |||
| builder: (context) { | |||
| return WillPopScope( | |||
| onWillPop: () async => false, | |||
| child: Opacity( | |||
| opacity: 1, | |||
| child: Stack( | |||
| children: <Widget>[ | |||
| Align( | |||
| alignment: Alignment.center, | |||
| child: Container( | |||
| decoration: new BoxDecoration( | |||
| color: Colors.white, | |||
| borderRadius: | |||
| new BorderRadius.all(Radius.circular(16.0))), | |||
| width: 80.0, | |||
| height: 80.0), | |||
| ), | |||
| Container( | |||
| alignment: Alignment.center, | |||
| child: FlutterLogo( | |||
| size: 25, | |||
| ), | |||
| ), | |||
| Container( | |||
| alignment: Alignment.center, | |||
| child: SizedBox( | |||
| child: CircularProgressIndicator( | |||
| strokeWidth: 2.0, | |||
| ), | |||
| width: 60.0, | |||
| height: 60.0, | |||
| ), | |||
| ) | |||
| ], | |||
| )), | |||
| ); | |||
| }); | |||
| } | |||
| static void hideLoadingDialog(BuildContext context) { | |||
| Navigator.pop(context, ""); | |||
| } | |||
| } | |||
| @@ -0,0 +1 @@ | |||
| export 'view/home_page.dart'; | |||
| @@ -0,0 +1,34 @@ | |||
| import 'package:farm_tpf/authentication/bloc/authentication_bloc.dart'; | |||
| import 'package:flutter/material.dart'; | |||
| import 'package:flutter_bloc/flutter_bloc.dart'; | |||
| class HomePage extends StatelessWidget { | |||
| static Route route() { | |||
| return MaterialPageRoute<void>(builder: (_) => HomePage()); | |||
| } | |||
| @override | |||
| Widget build(BuildContext context) { | |||
| return Scaffold( | |||
| appBar: AppBar(title: const Text('Home')), | |||
| body: Center( | |||
| child: Column( | |||
| mainAxisSize: MainAxisSize.min, | |||
| children: <Widget>[ | |||
| Text( | |||
| 'UserID: ${context.bloc<AuthenticationBloc>().state.user.id}', | |||
| ), | |||
| RaisedButton( | |||
| child: const Text('Đăng xuất'), | |||
| onPressed: () { | |||
| context | |||
| .bloc<AuthenticationBloc>() | |||
| .add(AuthenticationLogoutRequested()); | |||
| }, | |||
| ), | |||
| ], | |||
| ), | |||
| ), | |||
| ); | |||
| } | |||
| } | |||
| @@ -0,0 +1,75 @@ | |||
| import 'dart:async'; | |||
| import 'package:authentication_repository/authentication_repository.dart'; | |||
| import 'package:bloc/bloc.dart'; | |||
| import 'package:equatable/equatable.dart'; | |||
| import 'package:farm_tpf/presentation/screens/login/models/password.dart'; | |||
| import 'package:farm_tpf/presentation/screens/login/models/username.dart'; | |||
| import 'package:formz/formz.dart'; | |||
| import 'package:meta/meta.dart'; | |||
| part 'login_event.dart'; | |||
| part 'login_state.dart'; | |||
| class LoginBloc extends Bloc<LoginEvent, LoginState> { | |||
| LoginBloc({ | |||
| @required AuthenticationRepository authenticationRepository, | |||
| }) : assert(authenticationRepository != null), | |||
| _authenticationRepository = authenticationRepository, | |||
| super(const LoginState()); | |||
| final AuthenticationRepository _authenticationRepository; | |||
| @override | |||
| Stream<LoginState> mapEventToState( | |||
| LoginEvent event, | |||
| ) async* { | |||
| if (event is LoginUsernameChanged) { | |||
| yield _mapUsernameChangedToState(event, state); | |||
| } else if (event is LoginPasswordChanged) { | |||
| yield _mapPasswordChangedToState(event, state); | |||
| } else if (event is LoginSubmitted) { | |||
| yield* _mapLoginSubmittedToState(event, state); | |||
| } | |||
| } | |||
| LoginState _mapUsernameChangedToState( | |||
| LoginUsernameChanged event, | |||
| LoginState state, | |||
| ) { | |||
| final username = Username.dirty(event.username); | |||
| return state.copyWith( | |||
| username: username, | |||
| status: Formz.validate([state.password, username]), | |||
| ); | |||
| } | |||
| LoginState _mapPasswordChangedToState( | |||
| LoginPasswordChanged event, | |||
| LoginState state, | |||
| ) { | |||
| final password = Password.dirty(event.password); | |||
| return state.copyWith( | |||
| password: password, | |||
| status: Formz.validate([password, state.username]), | |||
| ); | |||
| } | |||
| Stream<LoginState> _mapLoginSubmittedToState( | |||
| LoginSubmitted event, | |||
| LoginState state, | |||
| ) async* { | |||
| if (state.status.isValidated) { | |||
| yield state.copyWith(status: FormzStatus.submissionInProgress); | |||
| try { | |||
| await _authenticationRepository.logIn( | |||
| username: state.username.value, | |||
| password: state.password.value, | |||
| ); | |||
| yield state.copyWith(status: FormzStatus.submissionSuccess); | |||
| } on Exception catch (_) { | |||
| yield state.copyWith(status: FormzStatus.submissionFailure); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,30 @@ | |||
| part of 'login_bloc.dart'; | |||
| abstract class LoginEvent extends Equatable { | |||
| const LoginEvent(); | |||
| @override | |||
| List<Object> get props => []; | |||
| } | |||
| class LoginUsernameChanged extends LoginEvent { | |||
| const LoginUsernameChanged(this.username); | |||
| final String username; | |||
| @override | |||
| List<Object> get props => [username]; | |||
| } | |||
| class LoginPasswordChanged extends LoginEvent { | |||
| const LoginPasswordChanged(this.password); | |||
| final String password; | |||
| @override | |||
| List<Object> get props => [password]; | |||
| } | |||
| class LoginSubmitted extends LoginEvent { | |||
| const LoginSubmitted(); | |||
| } | |||
| @@ -0,0 +1,28 @@ | |||
| part of 'login_bloc.dart'; | |||
| class LoginState extends Equatable { | |||
| const LoginState({ | |||
| this.status = FormzStatus.pure, | |||
| this.username = const Username.pure(), | |||
| this.password = const Password.pure(), | |||
| }); | |||
| final FormzStatus status; | |||
| final Username username; | |||
| final Password password; | |||
| LoginState copyWith({ | |||
| FormzStatus status, | |||
| Username username, | |||
| Password password, | |||
| }) { | |||
| return LoginState( | |||
| status: status ?? this.status, | |||
| username: username ?? this.username, | |||
| password: password ?? this.password, | |||
| ); | |||
| } | |||
| @override | |||
| List<Object> get props => [status, username, password]; | |||
| } | |||
| @@ -0,0 +1,3 @@ | |||
| export 'bloc/login_bloc.dart'; | |||
| export 'models/models.dart'; | |||
| export 'view/view.dart'; | |||
| @@ -0,0 +1,2 @@ | |||
| export 'password.dart'; | |||
| export 'username.dart'; | |||
| @@ -0,0 +1,13 @@ | |||
| import 'package:formz/formz.dart'; | |||
| enum PasswordValidationError { empty } | |||
| class Password extends FormzInput<String, PasswordValidationError> { | |||
| const Password.pure() : super.pure(''); | |||
| const Password.dirty([String value = '']) : super.dirty(value); | |||
| @override | |||
| PasswordValidationError validator(String value) { | |||
| return value?.isNotEmpty == true ? null : PasswordValidationError.empty; | |||
| } | |||
| } | |||
| @@ -0,0 +1,13 @@ | |||
| import 'package:formz/formz.dart'; | |||
| enum UsernameValidationError { empty } | |||
| class Username extends FormzInput<String, UsernameValidationError> { | |||
| const Username.pure() : super.pure(''); | |||
| const Username.dirty([String value = '']) : super.dirty(value); | |||
| @override | |||
| UsernameValidationError validator(String value) { | |||
| return value?.isNotEmpty == true ? null : UsernameValidationError.empty; | |||
| } | |||
| } | |||
| @@ -0,0 +1,98 @@ | |||
| import 'package:farm_tpf/presentation/screens/login/bloc/login_bloc.dart'; | |||
| import 'package:flutter/material.dart'; | |||
| import 'package:formz/formz.dart'; | |||
| import 'package:flutter_bloc/flutter_bloc.dart'; | |||
| class LoginForm extends StatelessWidget { | |||
| @override | |||
| Widget build(BuildContext context) { | |||
| return BlocListener<LoginBloc, LoginState>( | |||
| listener: (context, state) { | |||
| if (state.status.isSubmissionFailure) { | |||
| Scaffold.of(context) | |||
| ..hideCurrentSnackBar() | |||
| ..showSnackBar( | |||
| const SnackBar(content: Text('Authentication Failure')), | |||
| ); | |||
| } | |||
| }, | |||
| child: Align( | |||
| alignment: const Alignment(0, -1 / 3), | |||
| child: Column( | |||
| mainAxisSize: MainAxisSize.min, | |||
| children: [ | |||
| _UsernameInput(), | |||
| const Padding(padding: EdgeInsets.all(12)), | |||
| _PasswordInput(), | |||
| const Padding(padding: EdgeInsets.all(12)), | |||
| _LoginButton(), | |||
| ], | |||
| ), | |||
| ), | |||
| ); | |||
| } | |||
| } | |||
| class _UsernameInput extends StatelessWidget { | |||
| @override | |||
| Widget build(BuildContext context) { | |||
| return BlocBuilder<LoginBloc, LoginState>( | |||
| buildWhen: (previous, current) => previous.username != current.username, | |||
| builder: (context, state) { | |||
| return TextField( | |||
| key: const Key('loginForm_usernameInput_textField'), | |||
| onChanged: (username) => | |||
| context.bloc<LoginBloc>().add(LoginUsernameChanged(username)), | |||
| decoration: InputDecoration( | |||
| labelText: 'Tên đăng nhập', | |||
| errorText: | |||
| state.username.invalid ? 'Vui lòng nhập tên đăng nhập' : null, | |||
| ), | |||
| ); | |||
| }, | |||
| ); | |||
| } | |||
| } | |||
| class _PasswordInput extends StatelessWidget { | |||
| @override | |||
| Widget build(BuildContext context) { | |||
| return BlocBuilder<LoginBloc, LoginState>( | |||
| buildWhen: (previous, current) => previous.password != current.password, | |||
| builder: (context, state) { | |||
| return TextField( | |||
| key: const Key('loginForm_passwordInput_textField'), | |||
| onChanged: (password) => | |||
| context.bloc<LoginBloc>().add(LoginPasswordChanged(password)), | |||
| obscureText: true, | |||
| decoration: InputDecoration( | |||
| labelText: 'Mật khẩu', | |||
| errorText: state.password.invalid ? 'Vui lòng nhập mật khẩu' : null, | |||
| ), | |||
| ); | |||
| }, | |||
| ); | |||
| } | |||
| } | |||
| class _LoginButton extends StatelessWidget { | |||
| @override | |||
| Widget build(BuildContext context) { | |||
| return BlocBuilder<LoginBloc, LoginState>( | |||
| buildWhen: (previous, current) => previous.status != current.status, | |||
| builder: (context, state) { | |||
| return state.status.isSubmissionInProgress | |||
| ? const CircularProgressIndicator() | |||
| : RaisedButton( | |||
| key: const Key('loginForm_continue_raisedButton'), | |||
| child: const Text('Đăng nhập'), | |||
| onPressed: state.status.isValidated | |||
| ? () { | |||
| context.bloc<LoginBloc>().add(const LoginSubmitted()); | |||
| } | |||
| : null, | |||
| ); | |||
| }, | |||
| ); | |||
| } | |||
| } | |||
| @@ -0,0 +1,30 @@ | |||
| import 'package:authentication_repository/authentication_repository.dart'; | |||
| import 'package:farm_tpf/presentation/screens/login/bloc/login_bloc.dart'; | |||
| import 'package:flutter_bloc/flutter_bloc.dart'; | |||
| import 'package:flutter/material.dart'; | |||
| import 'login_form.dart'; | |||
| class LoginPage extends StatelessWidget { | |||
| static Route route() { | |||
| return MaterialPageRoute<void>(builder: (_) => LoginPage()); | |||
| } | |||
| @override | |||
| Widget build(BuildContext context) { | |||
| return Scaffold( | |||
| body: Padding( | |||
| padding: const EdgeInsets.all(12), | |||
| child: BlocProvider( | |||
| create: (context) { | |||
| return LoginBloc( | |||
| authenticationRepository: | |||
| RepositoryProvider.of<AuthenticationRepository>(context), | |||
| ); | |||
| }, | |||
| child: LoginForm(), | |||
| ), | |||
| ), | |||
| ); | |||
| } | |||
| } | |||
| @@ -0,0 +1,2 @@ | |||
| export 'login_form.dart'; | |||
| export 'login_page.dart'; | |||
| @@ -0,0 +1 @@ | |||
| export 'view/splash_page.dart'; | |||
| @@ -0,0 +1,14 @@ | |||
| import 'package:flutter/material.dart'; | |||
| class SplashPage extends StatelessWidget { | |||
| static Route route() { | |||
| return MaterialPageRoute<void>(builder: (_) => SplashPage()); | |||
| } | |||
| @override | |||
| Widget build(BuildContext context) { | |||
| return const Scaffold( | |||
| body: Center(child: CircularProgressIndicator()), | |||
| ); | |||
| } | |||
| } | |||
| @@ -0,0 +1,3 @@ | |||
| class ConstCommon { | |||
| static int kExpiredTime = 12 * 60 * 60 * 1000; //24h | |||
| } | |||
| @@ -23,4 +23,5 @@ abstract class Pref { | |||
| class PrefKey { | |||
| static const String token_key = "token_key"; | |||
| static const String expired_time = "expired_time"; | |||
| static int kExpiredTime = 12 * 60 * 60 * 1000; //24h | |||
| } | |||
| @@ -0,0 +1,3 @@ | |||
| library authentication_repository; | |||
| export 'src/authentication_repository.dart'; | |||
| @@ -0,0 +1,34 @@ | |||
| import 'dart:async'; | |||
| import 'package:meta/meta.dart'; | |||
| enum AuthenticationStatus { unknown, authenticated, unauthenticated } | |||
| class AuthenticationRepository { | |||
| final _controller = StreamController<AuthenticationStatus>(); | |||
| Stream<AuthenticationStatus> get status async* { | |||
| await Future<void>.delayed(const Duration(seconds: 1)); | |||
| yield AuthenticationStatus.unauthenticated; | |||
| yield* _controller.stream; | |||
| } | |||
| Future<void> logIn({ | |||
| @required String username, | |||
| @required String password, | |||
| }) async { | |||
| assert(username != null); | |||
| assert(password != null); | |||
| await Future.delayed( | |||
| const Duration(milliseconds: 300), | |||
| () => _controller.add(AuthenticationStatus.authenticated), | |||
| ); | |||
| } | |||
| void logOut() { | |||
| _controller.add(AuthenticationStatus.unauthenticated); | |||
| } | |||
| void dispose() => _controller.close(); | |||
| } | |||
| @@ -0,0 +1,12 @@ | |||
| # Generated by pub | |||
| # See https://dart.dev/tools/pub/glossary#lockfile | |||
| packages: | |||
| meta: | |||
| dependency: "direct main" | |||
| description: | |||
| name: meta | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "1.2.2" | |||
| sdks: | |||
| dart: ">=2.1.0 <3.0.0" | |||
| @@ -0,0 +1,9 @@ | |||
| name: authentication_repository | |||
| description: Dart package which manages the authentication domain. | |||
| version: 1.0.0 | |||
| environment: | |||
| sdk: ">=2.1.0 <3.0.0" | |||
| dependencies: | |||
| meta: ^1.1.8 | |||
| @@ -0,0 +1 @@ | |||
| export 'user.dart'; | |||
| @@ -0,0 +1,10 @@ | |||
| import 'package:equatable/equatable.dart'; | |||
| class User extends Equatable { | |||
| const User(this.id); | |||
| final String id; | |||
| @override | |||
| List<Object> get props => [id]; | |||
| } | |||
| @@ -0,0 +1,14 @@ | |||
| import 'dart:async'; | |||
| import 'models/models.dart'; | |||
| class UserRepository { | |||
| User _user; | |||
| Future<User> getUser() async { | |||
| if (_user != null) return _user; | |||
| return Future.delayed( | |||
| const Duration(milliseconds: 300), | |||
| () => _user = User("usertest"), | |||
| ); | |||
| } | |||
| } | |||
| @@ -0,0 +1,4 @@ | |||
| library user_repository; | |||
| export 'src/models/models.dart'; | |||
| export 'src/user_repository.dart'; | |||
| @@ -0,0 +1,61 @@ | |||
| # Generated by pub | |||
| # See https://dart.dev/tools/pub/glossary#lockfile | |||
| packages: | |||
| charcode: | |||
| dependency: transitive | |||
| description: | |||
| name: charcode | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "1.1.3" | |||
| collection: | |||
| dependency: transitive | |||
| description: | |||
| name: collection | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "1.14.13" | |||
| convert: | |||
| dependency: transitive | |||
| description: | |||
| name: convert | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "2.1.1" | |||
| crypto: | |||
| dependency: transitive | |||
| description: | |||
| name: crypto | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "2.1.5" | |||
| equatable: | |||
| dependency: "direct main" | |||
| description: | |||
| name: equatable | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "1.2.3" | |||
| meta: | |||
| dependency: "direct main" | |||
| description: | |||
| name: meta | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "1.2.2" | |||
| typed_data: | |||
| dependency: transitive | |||
| description: | |||
| name: typed_data | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "1.2.0" | |||
| uuid: | |||
| dependency: "direct main" | |||
| description: | |||
| name: uuid | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "2.2.0" | |||
| sdks: | |||
| dart: ">=2.4.0 <3.0.0" | |||
| @@ -0,0 +1,11 @@ | |||
| name: user_repository | |||
| description: Dart package which manages the user domain. | |||
| version: 1.0.0 | |||
| environment: | |||
| sdk: ">=2.1.0 <3.0.0" | |||
| dependencies: | |||
| equatable: ^1.2.0 | |||
| meta: ^1.1.8 | |||
| uuid: ^2.1.0 | |||
| @@ -1,6 +1,20 @@ | |||
| # Generated by pub | |||
| # See https://dart.dev/tools/pub/glossary#lockfile | |||
| packages: | |||
| _fe_analyzer_shared: | |||
| dependency: transitive | |||
| description: | |||
| name: _fe_analyzer_shared | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "6.0.0" | |||
| analyzer: | |||
| dependency: transitive | |||
| description: | |||
| name: analyzer | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "0.39.16" | |||
| archive: | |||
| dependency: transitive | |||
| description: | |||
| @@ -22,6 +36,20 @@ packages: | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "2.4.1" | |||
| authentication_repository: | |||
| dependency: "direct main" | |||
| description: | |||
| path: "packages/authentication_repository" | |||
| relative: true | |||
| source: path | |||
| version: "1.0.0" | |||
| bloc: | |||
| dependency: transitive | |||
| description: | |||
| name: bloc | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "6.0.1" | |||
| boolean_selector: | |||
| dependency: transitive | |||
| description: | |||
| @@ -29,6 +57,62 @@ packages: | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "2.0.0" | |||
| build: | |||
| dependency: transitive | |||
| description: | |||
| name: build | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "1.3.0" | |||
| build_config: | |||
| dependency: transitive | |||
| description: | |||
| name: build_config | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "0.4.2" | |||
| build_daemon: | |||
| dependency: transitive | |||
| description: | |||
| name: build_daemon | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "2.1.4" | |||
| build_resolvers: | |||
| dependency: transitive | |||
| description: | |||
| name: build_resolvers | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "1.3.10" | |||
| build_runner: | |||
| dependency: "direct dev" | |||
| description: | |||
| name: build_runner | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "1.10.0" | |||
| build_runner_core: | |||
| dependency: transitive | |||
| description: | |||
| name: build_runner_core | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "5.2.0" | |||
| built_collection: | |||
| dependency: transitive | |||
| description: | |||
| name: built_collection | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "4.3.2" | |||
| built_value: | |||
| dependency: transitive | |||
| description: | |||
| name: built_value | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "7.1.0" | |||
| charcode: | |||
| dependency: transitive | |||
| description: | |||
| @@ -36,6 +120,27 @@ packages: | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "1.1.3" | |||
| checked_yaml: | |||
| dependency: transitive | |||
| description: | |||
| name: checked_yaml | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "1.0.2" | |||
| cli_util: | |||
| dependency: transitive | |||
| description: | |||
| name: cli_util | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "0.1.4" | |||
| code_builder: | |||
| dependency: transitive | |||
| description: | |||
| name: code_builder | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "3.4.0" | |||
| collection: | |||
| dependency: transitive | |||
| description: | |||
| @@ -57,6 +162,13 @@ packages: | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "2.1.4" | |||
| csslib: | |||
| dependency: transitive | |||
| description: | |||
| name: csslib | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "0.16.2" | |||
| cupertino_icons: | |||
| dependency: "direct main" | |||
| description: | |||
| @@ -64,6 +176,27 @@ packages: | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "0.1.3" | |||
| dart_style: | |||
| dependency: transitive | |||
| description: | |||
| name: dart_style | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "1.3.6" | |||
| dio: | |||
| dependency: "direct main" | |||
| description: | |||
| name: dio | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "3.0.9" | |||
| equatable: | |||
| dependency: "direct main" | |||
| description: | |||
| name: equatable | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "1.2.3" | |||
| file: | |||
| dependency: transitive | |||
| description: | |||
| @@ -71,11 +204,25 @@ packages: | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "5.2.1" | |||
| fixnum: | |||
| dependency: transitive | |||
| description: | |||
| name: fixnum | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "0.10.11" | |||
| flutter: | |||
| dependency: "direct main" | |||
| description: flutter | |||
| source: sdk | |||
| version: "0.0.0" | |||
| flutter_bloc: | |||
| dependency: "direct main" | |||
| description: | |||
| name: flutter_bloc | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "6.0.1" | |||
| flutter_test: | |||
| dependency: "direct dev" | |||
| description: flutter | |||
| @@ -86,6 +233,48 @@ packages: | |||
| description: flutter | |||
| source: sdk | |||
| version: "0.0.0" | |||
| formz: | |||
| dependency: "direct main" | |||
| description: | |||
| name: formz | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "0.3.0" | |||
| glob: | |||
| dependency: transitive | |||
| description: | |||
| name: glob | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "1.2.0" | |||
| graphs: | |||
| dependency: transitive | |||
| description: | |||
| name: graphs | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "0.2.0" | |||
| html: | |||
| dependency: transitive | |||
| description: | |||
| name: html | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "0.14.0+3" | |||
| http_multi_server: | |||
| dependency: transitive | |||
| description: | |||
| name: http_multi_server | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "2.2.0" | |||
| http_parser: | |||
| dependency: transitive | |||
| description: | |||
| name: http_parser | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "3.1.4" | |||
| image: | |||
| dependency: transitive | |||
| description: | |||
| @@ -100,6 +289,41 @@ packages: | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "0.16.1" | |||
| io: | |||
| dependency: transitive | |||
| description: | |||
| name: io | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "0.3.4" | |||
| js: | |||
| dependency: transitive | |||
| description: | |||
| name: js | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "0.6.2" | |||
| json_annotation: | |||
| dependency: transitive | |||
| description: | |||
| name: json_annotation | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "3.0.1" | |||
| json_serializable: | |||
| dependency: "direct dev" | |||
| description: | |||
| name: json_serializable | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "3.3.0" | |||
| logging: | |||
| dependency: transitive | |||
| description: | |||
| name: logging | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "0.11.4" | |||
| matcher: | |||
| dependency: transitive | |||
| description: | |||
| @@ -114,6 +338,13 @@ packages: | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "1.1.8" | |||
| mime: | |||
| dependency: transitive | |||
| description: | |||
| name: mime | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "0.9.6+3" | |||
| nested: | |||
| dependency: transitive | |||
| description: | |||
| @@ -121,6 +352,27 @@ packages: | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "0.0.4" | |||
| node_interop: | |||
| dependency: transitive | |||
| description: | |||
| name: node_interop | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "1.1.1" | |||
| node_io: | |||
| dependency: transitive | |||
| description: | |||
| name: node_io | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "1.1.1" | |||
| package_config: | |||
| dependency: transitive | |||
| description: | |||
| name: package_config | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "1.9.3" | |||
| path: | |||
| dependency: transitive | |||
| description: | |||
| @@ -142,6 +394,13 @@ packages: | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "1.0.2" | |||
| pedantic: | |||
| dependency: transitive | |||
| description: | |||
| name: pedantic | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "1.9.0" | |||
| petitparser: | |||
| dependency: transitive | |||
| description: | |||
| @@ -163,6 +422,13 @@ packages: | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "1.0.2" | |||
| pool: | |||
| dependency: transitive | |||
| description: | |||
| name: pool | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "1.4.0" | |||
| process: | |||
| dependency: transitive | |||
| description: | |||
| @@ -171,12 +437,26 @@ packages: | |||
| source: hosted | |||
| version: "3.0.13" | |||
| provider: | |||
| dependency: "direct main" | |||
| dependency: transitive | |||
| description: | |||
| name: provider | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "4.3.1" | |||
| pub_semver: | |||
| dependency: transitive | |||
| description: | |||
| name: pub_semver | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "1.4.4" | |||
| pubspec_parse: | |||
| dependency: transitive | |||
| description: | |||
| name: pubspec_parse | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "0.1.5" | |||
| quiver: | |||
| dependency: transitive | |||
| description: | |||
| @@ -184,6 +464,20 @@ packages: | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "2.1.3" | |||
| retrofit: | |||
| dependency: transitive | |||
| description: | |||
| name: retrofit | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "1.3.4" | |||
| retrofit_generator: | |||
| dependency: "direct dev" | |||
| description: | |||
| name: retrofit_generator | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "1.3.7+5" | |||
| shared_preferences: | |||
| dependency: "direct main" | |||
| description: | |||
| @@ -219,11 +513,32 @@ packages: | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "0.1.2+7" | |||
| shelf: | |||
| dependency: transitive | |||
| description: | |||
| name: shelf | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "0.7.7" | |||
| shelf_web_socket: | |||
| dependency: transitive | |||
| description: | |||
| name: shelf_web_socket | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "0.2.3" | |||
| sky_engine: | |||
| dependency: transitive | |||
| description: flutter | |||
| source: sdk | |||
| version: "0.0.99" | |||
| source_gen: | |||
| dependency: transitive | |||
| description: | |||
| name: source_gen | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "0.9.6" | |||
| source_span: | |||
| dependency: transitive | |||
| description: | |||
| @@ -245,6 +560,13 @@ packages: | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "2.0.0" | |||
| stream_transform: | |||
| dependency: transitive | |||
| description: | |||
| name: stream_transform | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "1.2.0" | |||
| string_scanner: | |||
| dependency: transitive | |||
| description: | |||
| @@ -266,6 +588,20 @@ packages: | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "0.2.15" | |||
| timing: | |||
| dependency: transitive | |||
| description: | |||
| name: timing | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "0.1.1+2" | |||
| tuple: | |||
| dependency: transitive | |||
| description: | |||
| name: tuple | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "1.0.3" | |||
| typed_data: | |||
| dependency: transitive | |||
| description: | |||
| @@ -273,6 +609,20 @@ packages: | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "1.1.6" | |||
| user_repository: | |||
| dependency: "direct main" | |||
| description: | |||
| path: "packages/user_repository" | |||
| relative: true | |||
| source: path | |||
| version: "1.0.0" | |||
| uuid: | |||
| dependency: transitive | |||
| description: | |||
| name: uuid | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "2.2.0" | |||
| vector_math: | |||
| dependency: transitive | |||
| description: | |||
| @@ -280,6 +630,20 @@ packages: | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "2.0.8" | |||
| watcher: | |||
| dependency: transitive | |||
| description: | |||
| name: watcher | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "0.9.7+15" | |||
| web_socket_channel: | |||
| dependency: transitive | |||
| description: | |||
| name: web_socket_channel | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "1.1.0" | |||
| xdg_directories: | |||
| dependency: transitive | |||
| description: | |||
| @@ -294,6 +658,13 @@ packages: | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "3.6.1" | |||
| yaml: | |||
| dependency: transitive | |||
| description: | |||
| name: yaml | |||
| url: "https://pub.dartlang.org" | |||
| source: hosted | |||
| version: "2.2.1" | |||
| sdks: | |||
| dart: ">=2.7.0 <3.0.0" | |||
| flutter: ">=1.16.0 <2.0.0" | |||
| @@ -1,20 +1,7 @@ | |||
| name: farm_tpf | |||
| description: A new Flutter project. | |||
| # The following line prevents the package from being accidentally published to | |||
| # pub.dev using `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: | |||
| @@ -23,56 +10,26 @@ 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.3 | |||
| provider: ^4.3.1 | |||
| shared_preferences: ^0.5.8 | |||
| flutter_bloc: ^6.0.1 | |||
| equatable: ^1.2.0 | |||
| dio: 3.0.9 | |||
| formz: ^0.3.0 | |||
| authentication_repository: | |||
| path: packages/authentication_repository | |||
| user_repository: | |||
| path: packages/user_repository | |||
| dev_dependencies: | |||
| flutter_test: | |||
| sdk: flutter | |||
| # 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. | |||
| retrofit_generator: ^1.3.7 | |||
| # json_model: ^0.0.2 | |||
| build_runner: any | |||
| json_serializable: any | |||
| 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: | |||
| # - 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 | |||
| uses-material-design: true | |||
| @@ -1,31 +1 @@ | |||
| // This is a basic Flutter widget test. | |||
| // | |||
| // To perform an interaction with a widget in your test, use the WidgetTester | |||
| // utility that Flutter provides. For example, you can send tap and scroll | |||
| // gestures. You can also use WidgetTester to find child widgets in the widget | |||
| // tree, read text, and verify that the values of widget properties are correct. | |||
| import 'package:farm_tpf/app/my_app.dart'; | |||
| import 'package:flutter/material.dart'; | |||
| import 'package:flutter_test/flutter_test.dart'; | |||
| import 'package:farm_tpf/main.dart'; | |||
| void main() { | |||
| testWidgets('Counter increments smoke test', (WidgetTester tester) async { | |||
| // Build our app and trigger a frame. | |||
| await tester.pumpWidget(MyApp()); | |||
| // Verify that our counter starts at 0. | |||
| expect(find.text('0'), findsOneWidget); | |||
| expect(find.text('1'), findsNothing); | |||
| // Tap the '+' icon and trigger a frame. | |||
| await tester.tap(find.byIcon(Icons.add)); | |||
| await tester.pump(); | |||
| // Verify that our counter has incremented. | |||
| expect(find.text('0'), findsNothing); | |||
| expect(find.text('1'), findsOneWidget); | |||
| }); | |||
| } | |||