mirror of
				https://gitlab.com/comunic/comunicmobile
				synced 2025-11-04 04:04:18 +00:00 
			
		
		
		
	Can sign in user
This commit is contained in:
		@@ -1,3 +1,8 @@
 | 
				
			|||||||
 | 
					import 'dart:convert';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import 'package:comunic/models/login_tokens.dart';
 | 
				
			||||||
 | 
					import 'package:shared_preferences/shared_preferences.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Accounts credentials helper
 | 
					/// Accounts credentials helper
 | 
				
			||||||
///
 | 
					///
 | 
				
			||||||
/// Stores current account tokens
 | 
					/// Stores current account tokens
 | 
				
			||||||
@@ -8,7 +13,27 @@ class AccountCredentialsHelper {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  /// Checkout whether current user is signed in or not
 | 
					  /// Checkout whether current user is signed in or not
 | 
				
			||||||
  Future<bool> signedIn() async {
 | 
					  Future<bool> signedIn() async {
 | 
				
			||||||
    return false; // TODO : implement
 | 
					    return await get() != null;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Set new login tokens
 | 
				
			||||||
 | 
					  Future<void> set(LoginTokens tokens) async {
 | 
				
			||||||
 | 
					    SharedPreferences prefs = await SharedPreferences.getInstance();
 | 
				
			||||||
 | 
					    await prefs.setString("login_tokens", tokens.toString());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Get current [LoginTokens]. Returns null if none or in case of failure
 | 
				
			||||||
 | 
					  Future<LoginTokens> get() async {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      SharedPreferences prefs = await SharedPreferences.getInstance();
 | 
				
			||||||
 | 
					      final string = prefs.getString("login_tokens");
 | 
				
			||||||
 | 
					      if(string == null) return null;
 | 
				
			||||||
 | 
					      return LoginTokens.fromJSON(jsonDecode(string));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    } on Exception catch(e){
 | 
				
			||||||
 | 
					      print(e.toString());
 | 
				
			||||||
 | 
					      return null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
							
								
								
									
										41
									
								
								lib/helpers/account_helper.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								lib/helpers/account_helper.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,41 @@
 | 
				
			|||||||
 | 
					import 'package:comunic/helpers/account_credentials_helper.dart';
 | 
				
			||||||
 | 
					import 'package:comunic/helpers/api_helper.dart';
 | 
				
			||||||
 | 
					import 'package:comunic/models/api_request.dart';
 | 
				
			||||||
 | 
					import 'package:comunic/models/authentication_details.dart';
 | 
				
			||||||
 | 
					import 'package:comunic/models/login_tokens.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Account helper
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// @author Pierre HUBERT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum AuthResult {
 | 
				
			||||||
 | 
					  SUCCESS,
 | 
				
			||||||
 | 
					  TOO_MANY_ATTEMPTS,
 | 
				
			||||||
 | 
					  NETWORK_ERROR,
 | 
				
			||||||
 | 
					  INVALID_CREDENTIALS
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class AccountHelper {
 | 
				
			||||||
 | 
					  /// Sign in user
 | 
				
			||||||
 | 
					  Future<AuthResult> signIn(AuthenticationDetails auth) async {
 | 
				
			||||||
 | 
					    final request = APIRequest(uri: "account/login");
 | 
				
			||||||
 | 
					    request.addString("userMail", auth.email);
 | 
				
			||||||
 | 
					    request.addString("userPassword", auth.password);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    final response = await APIHelper().exec(request);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Handle errors
 | 
				
			||||||
 | 
					    if (response.code == 401)
 | 
				
			||||||
 | 
					      return AuthResult.INVALID_CREDENTIALS;
 | 
				
			||||||
 | 
					    else if (response.code == 429)
 | 
				
			||||||
 | 
					      return AuthResult.TOO_MANY_ATTEMPTS;
 | 
				
			||||||
 | 
					    else if (response.code != 200) return AuthResult.NETWORK_ERROR;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //Save login tokens
 | 
				
			||||||
 | 
					    final tokensObj = response.getObject()["tokens"];
 | 
				
			||||||
 | 
					    await AccountCredentialsHelper()
 | 
				
			||||||
 | 
					        .set(LoginTokens(tokensObj["token1"], tokensObj["token2"]));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return AuthResult.SUCCESS;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										68
									
								
								lib/helpers/api_helper.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								lib/helpers/api_helper.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,68 @@
 | 
				
			|||||||
 | 
					import 'dart:convert';
 | 
				
			||||||
 | 
					import 'dart:io';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import 'package:comunic/models/api_request.dart';
 | 
				
			||||||
 | 
					import 'package:comunic/models/api_response.dart';
 | 
				
			||||||
 | 
					import 'package:comunic/models/config.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// API Helper
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// @author Pierre HUBERT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class APIHelper {
 | 
				
			||||||
 | 
					  final _httpClient = HttpClient();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Execute a [request] on the server and returns a [APIResponse]
 | 
				
			||||||
 | 
					  ///
 | 
				
			||||||
 | 
					  /// This method should never throw but the response code of the [APIResponse]
 | 
				
			||||||
 | 
					  /// should be verified before accessing response content
 | 
				
			||||||
 | 
					  Future<APIResponse> exec(APIRequest request) async {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      //Add API tokens
 | 
				
			||||||
 | 
					      request.addString("serviceName", config().serviceName);
 | 
				
			||||||
 | 
					      request.addString("serviceToken", config().serviceToken);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      //Add user tokens (if required)
 | 
				
			||||||
 | 
					      if (request.needLogin) throw "Can add user tokens right now !";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // Prepare request body
 | 
				
			||||||
 | 
					      String requestBody = "";
 | 
				
			||||||
 | 
					      request.args.forEach((key, value) => requestBody +=
 | 
				
			||||||
 | 
					          Uri.encodeQueryComponent(key) +
 | 
				
			||||||
 | 
					              "=" +
 | 
				
			||||||
 | 
					              Uri.encodeQueryComponent(value) +
 | 
				
			||||||
 | 
					              "&");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      List<int> bodyBytes = utf8.encode(requestBody);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // Determine server URL
 | 
				
			||||||
 | 
					      final path = config().apiServerUri + request.uri;
 | 
				
			||||||
 | 
					      Uri url;
 | 
				
			||||||
 | 
					      if (!config().apiServerSecure)
 | 
				
			||||||
 | 
					        url = Uri.http(config().apiServerName, path);
 | 
				
			||||||
 | 
					      else
 | 
				
			||||||
 | 
					        url = Uri.https(config().apiServerName, path);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      //Connect to server
 | 
				
			||||||
 | 
					      final connection = await _httpClient.postUrl(url);
 | 
				
			||||||
 | 
					      connection.headers.set("Content-Length", bodyBytes.length.toString());
 | 
				
			||||||
 | 
					      connection.headers
 | 
				
			||||||
 | 
					          .set("Content-Type", "application/x-www-form-urlencoded");
 | 
				
			||||||
 | 
					      connection.add(bodyBytes);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      final response = await connection.close();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (response.statusCode != HttpStatus.ok)
 | 
				
			||||||
 | 
					        return APIResponse(response.statusCode, null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      //Save & return response
 | 
				
			||||||
 | 
					      final responseBody = await response.transform(utf8.decoder).join();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return APIResponse(response.statusCode, responseBody);
 | 
				
			||||||
 | 
					    } on Exception catch (e) {
 | 
				
			||||||
 | 
					      print(e.toString());
 | 
				
			||||||
 | 
					      print("Could not execute a request!");
 | 
				
			||||||
 | 
					      return APIResponse(-1, null);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,5 +1,8 @@
 | 
				
			|||||||
import 'package:comunic/helpers/account_credentials_helper.dart';
 | 
					import 'package:comunic/helpers/account_credentials_helper.dart';
 | 
				
			||||||
import 'package:comunic/ui/login_route.dart';
 | 
					import 'package:comunic/models/config.dart';
 | 
				
			||||||
 | 
					import 'package:comunic/ui/routes/home_route.dart';
 | 
				
			||||||
 | 
					import 'package:comunic/ui/routes/login_route.dart';
 | 
				
			||||||
 | 
					import 'package:comunic/utils/ui_utils.dart';
 | 
				
			||||||
import 'package:flutter/material.dart';
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Main file of the application
 | 
					/// Main file of the application
 | 
				
			||||||
@@ -7,6 +10,16 @@ import 'package:flutter/material.dart';
 | 
				
			|||||||
/// @author Pierre HUBERT
 | 
					/// @author Pierre HUBERT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void main() {
 | 
					void main() {
 | 
				
			||||||
 | 
					  /// Initialize application configuration
 | 
				
			||||||
 | 
					  ///
 | 
				
			||||||
 | 
					  // TODO : Adapt to production
 | 
				
			||||||
 | 
					  Config.set(Config(
 | 
				
			||||||
 | 
					      apiServerName: "devweb.local",
 | 
				
			||||||
 | 
					      apiServerUri: "/comunic/api/",
 | 
				
			||||||
 | 
					      apiServerSecure: false,
 | 
				
			||||||
 | 
					      serviceName: "ComunicFlutter",
 | 
				
			||||||
 | 
					      serviceToken: "G9sZCBmb3IgVWJ1bnR1CkNvbW1lbnRbbmVdPeCkieCkrOCkq"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  runApp(ComunicApplication());
 | 
					  runApp(ComunicApplication());
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -15,20 +28,37 @@ class ComunicApplication extends StatelessWidget {
 | 
				
			|||||||
  Widget build(BuildContext context) {
 | 
					  Widget build(BuildContext context) {
 | 
				
			||||||
    return MaterialApp(
 | 
					    return MaterialApp(
 | 
				
			||||||
      debugShowCheckedModeBanner: false,
 | 
					      debugShowCheckedModeBanner: false,
 | 
				
			||||||
      // We need to know whether the user is signed in or not to continue
 | 
					      home: ComunicApplicationHome(),
 | 
				
			||||||
      home: FutureBuilder(
 | 
					 | 
				
			||||||
        future: AccountCredentialsHelper().signedIn(),
 | 
					 | 
				
			||||||
        builder: (b, AsyncSnapshot<bool> c){
 | 
					 | 
				
			||||||
          if(!c.hasData)
 | 
					 | 
				
			||||||
            return CircularProgressIndicator();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          else
 | 
					 | 
				
			||||||
            if(c.data)
 | 
					 | 
				
			||||||
              throw "Not supported yet !";
 | 
					 | 
				
			||||||
            else
 | 
					 | 
				
			||||||
              return LoginRoute();
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
      ),
 | 
					 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ComunicApplicationHome extends StatefulWidget {
 | 
				
			||||||
 | 
					  @override
 | 
				
			||||||
 | 
					  State<StatefulWidget> createState() => _ComunicApplicationHomeState();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class _ComunicApplicationHomeState extends State<ComunicApplicationHome> {
 | 
				
			||||||
 | 
					  bool _signedIn;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override
 | 
				
			||||||
 | 
					  void initState() {
 | 
				
			||||||
 | 
					    super.initState();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    AccountCredentialsHelper().signedIn().then((v) {
 | 
				
			||||||
 | 
					      setState(() {
 | 
				
			||||||
 | 
					        _signedIn = v;
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override
 | 
				
			||||||
 | 
					  Widget build(BuildContext context) {
 | 
				
			||||||
 | 
					    if (_signedIn == null) return buildLoadingPage();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (_signedIn)
 | 
				
			||||||
 | 
					      return HomeRoute();
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					      return LoginRoute();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										27
									
								
								lib/models/api_request.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								lib/models/api_request.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
				
			|||||||
 | 
					import 'package:meta/meta.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// API Request model
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// Contains all the information associated to an API request
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// @author Pierre HUBERT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class APIRequest {
 | 
				
			||||||
 | 
					  final String uri;
 | 
				
			||||||
 | 
					  final bool needLogin;
 | 
				
			||||||
 | 
					  Map<String, String> args;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  APIRequest({
 | 
				
			||||||
 | 
					    @required this.uri,
 | 
				
			||||||
 | 
					    this.needLogin = false,
 | 
				
			||||||
 | 
					  })  : assert(uri != null),
 | 
				
			||||||
 | 
					        assert(needLogin != null),
 | 
				
			||||||
 | 
					        args = Map();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void addString(String name, String value) => args[name] = value;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void addInt(String name, int value) => args[name] = value.toString();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void addBool(String name, bool value) =>
 | 
				
			||||||
 | 
					      args[name] = value ? "true" : "false";
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										16
									
								
								lib/models/api_response.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								lib/models/api_response.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
				
			|||||||
 | 
					import 'dart:convert';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// API response
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// @author Pierre HUBERT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class APIResponse {
 | 
				
			||||||
 | 
					  final int code;
 | 
				
			||||||
 | 
					  final String content;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const APIResponse(this.code, this.content) : assert(code != null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  List<dynamic> getArray() => jsonDecode(this.content);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Map<String, dynamic> getObject() => jsonDecode(this.content);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										14
									
								
								lib/models/authentication_details.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								lib/models/authentication_details.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
				
			|||||||
 | 
					import 'package:meta/meta.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Authentication details
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// @author Pierre HUBERT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class AuthenticationDetails {
 | 
				
			||||||
 | 
					  final String email;
 | 
				
			||||||
 | 
					  final String password;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const AuthenticationDetails({@required this.email, @required this.password})
 | 
				
			||||||
 | 
					      : assert(email != null),
 | 
				
			||||||
 | 
					        assert(password != null);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										42
									
								
								lib/models/config.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								lib/models/config.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,42 @@
 | 
				
			|||||||
 | 
					import 'package:meta/meta.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Application configuration model
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// @author Pierre HUBERT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Configuration class
 | 
				
			||||||
 | 
					class Config {
 | 
				
			||||||
 | 
					  final String apiServerName;
 | 
				
			||||||
 | 
					  final String apiServerUri;
 | 
				
			||||||
 | 
					  final bool apiServerSecure;
 | 
				
			||||||
 | 
					  final String serviceName;
 | 
				
			||||||
 | 
					  final String serviceToken;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const Config(
 | 
				
			||||||
 | 
					      {@required this.apiServerName,
 | 
				
			||||||
 | 
					      @required this.apiServerUri,
 | 
				
			||||||
 | 
					      @required this.apiServerSecure,
 | 
				
			||||||
 | 
					      @required this.serviceName,
 | 
				
			||||||
 | 
					      @required this.serviceToken})
 | 
				
			||||||
 | 
					      : assert(apiServerName != null),
 | 
				
			||||||
 | 
					        assert(apiServerUri != null),
 | 
				
			||||||
 | 
					        assert(apiServerSecure != null),
 | 
				
			||||||
 | 
					        assert(serviceName != null),
 | 
				
			||||||
 | 
					        assert(serviceToken != null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Get and set static configuration
 | 
				
			||||||
 | 
					  static Config _config;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  static Config get() {
 | 
				
			||||||
 | 
					    return _config;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  static void set(Config conf) {
 | 
				
			||||||
 | 
					    _config = conf;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Get the current configuration of the application
 | 
				
			||||||
 | 
					Config config() {
 | 
				
			||||||
 | 
					  return Config.get();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										23
									
								
								lib/models/login_tokens.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								lib/models/login_tokens.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
				
			|||||||
 | 
					import 'dart:convert';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Login tokens model
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// @author Pierre HUBERT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class LoginTokens {
 | 
				
			||||||
 | 
					  final String tokenOne;
 | 
				
			||||||
 | 
					  final String tokenTwo;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const LoginTokens(this.tokenOne, this.tokenTwo)
 | 
				
			||||||
 | 
					      : assert(tokenOne != null),
 | 
				
			||||||
 | 
					        assert(tokenTwo != null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  LoginTokens.fromJSON(Map<String, dynamic> json)
 | 
				
			||||||
 | 
					      : tokenOne = json["token_one"],
 | 
				
			||||||
 | 
					        tokenTwo = json["token_two"];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override
 | 
				
			||||||
 | 
					  String toString() {
 | 
				
			||||||
 | 
					    return jsonEncode({"token_one": tokenOne, "token_two": tokenTwo});
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										12
									
								
								lib/ui/routes/home_route.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								lib/ui/routes/home_route.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Main route of the application
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// @author Pierre HUBERT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class HomeRoute extends StatelessWidget {
 | 
				
			||||||
 | 
					  @override
 | 
				
			||||||
 | 
					  Widget build(BuildContext context) {
 | 
				
			||||||
 | 
					    return Text("Home route");
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,5 +1,9 @@
 | 
				
			|||||||
 | 
					import 'package:comunic/helpers/account_helper.dart';
 | 
				
			||||||
 | 
					import 'package:comunic/models/authentication_details.dart';
 | 
				
			||||||
 | 
					import 'package:comunic/ui/routes/home_route.dart';
 | 
				
			||||||
import 'package:comunic/utils/input_utils.dart';
 | 
					import 'package:comunic/utils/input_utils.dart';
 | 
				
			||||||
import 'package:comunic/utils/intl_utils.dart';
 | 
					import 'package:comunic/utils/intl_utils.dart';
 | 
				
			||||||
 | 
					import 'package:comunic/utils/ui_utils.dart';
 | 
				
			||||||
import 'package:flutter/material.dart';
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Login route
 | 
					/// Login route
 | 
				
			||||||
@@ -14,6 +18,7 @@ class LoginRoute extends StatefulWidget {
 | 
				
			|||||||
class _LoginRouteState extends State<LoginRoute> {
 | 
					class _LoginRouteState extends State<LoginRoute> {
 | 
				
			||||||
  String _currEmail;
 | 
					  String _currEmail;
 | 
				
			||||||
  String _currPassword;
 | 
					  String _currPassword;
 | 
				
			||||||
 | 
					  AuthResult _authResult;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  void initState() {
 | 
					  void initState() {
 | 
				
			||||||
@@ -35,26 +40,51 @@ class _LoginRouteState extends State<LoginRoute> {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Call this whenever the user request to perform login
 | 
					  /// Call this whenever the user request to perform login
 | 
				
			||||||
  void _submitForm() {
 | 
					  Future<void> _submitForm(BuildContext context) async {
 | 
				
			||||||
 | 
					    final loginResult = await AccountHelper().signIn(
 | 
				
			||||||
 | 
					        AuthenticationDetails(email: _currEmail, password: _currPassword));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (loginResult == AuthResult.SUCCESS)
 | 
				
			||||||
 | 
					      Navigator.pushReplacement(
 | 
				
			||||||
 | 
					          context, MaterialPageRoute(builder: (b) => HomeRoute()));
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					      setState(() {
 | 
				
			||||||
 | 
					        _authResult = loginResult;
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Build error card
 | 
				
			||||||
 | 
					  Widget _buildErrorCard() {
 | 
				
			||||||
 | 
					    if (_authResult == null)
 | 
				
			||||||
 | 
					      return null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //Determine the right message
 | 
				
			||||||
 | 
					    final message = (_authResult == AuthResult.INVALID_CREDENTIALS ? tr(
 | 
				
			||||||
 | 
					        "Invalid credentials!") : (_authResult == AuthResult.TOO_MANY_ATTEMPTS
 | 
				
			||||||
 | 
					        ? tr("Too many unsuccessfull login attempts! Please try again later...")
 | 
				
			||||||
 | 
					        : tr("A network error occured!")));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return buildErrorCard(message);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Build login form
 | 
					  /// Build login form
 | 
				
			||||||
  Widget _buildLoginForm(){
 | 
					  Widget _buildLoginForm() {
 | 
				
			||||||
    return SingleChildScrollView(
 | 
					    return SingleChildScrollView(
 | 
				
			||||||
      child: Padding(
 | 
					      child: Padding(
 | 
				
			||||||
        padding: const EdgeInsets.all(8.0),
 | 
					        padding: const EdgeInsets.all(8.0),
 | 
				
			||||||
        child: Column(
 | 
					        child: Column(
 | 
				
			||||||
          children: <Widget>[
 | 
					          children: <Widget>[
 | 
				
			||||||
            Text(tr("Please sign into your Comunic account: ")),
 | 
					            Text(tr("Please sign into your Comunic account: ")),
 | 
				
			||||||
 | 
					            Container(
 | 
				
			||||||
 | 
					              child: _buildErrorCard(),
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
            //Email address
 | 
					            //Email address
 | 
				
			||||||
            TextField(
 | 
					            TextField(
 | 
				
			||||||
              keyboardType: TextInputType.emailAddress,
 | 
					              keyboardType: TextInputType.emailAddress,
 | 
				
			||||||
              decoration: InputDecoration(
 | 
					              decoration: InputDecoration(
 | 
				
			||||||
                  labelText: tr("Email address"),
 | 
					                  labelText: tr("Email address"),
 | 
				
			||||||
                  alignLabelWithHint: true,
 | 
					                  alignLabelWithHint: true,
 | 
				
			||||||
                  errorText:
 | 
					                  errorText: _currEmail.length > 0 && !validateEmail(_currEmail)
 | 
				
			||||||
                  _currEmail.length > 0 && !validateEmail(_currEmail)
 | 
					 | 
				
			||||||
                      ? tr("Invalid email address!")
 | 
					                      ? tr("Invalid email address!")
 | 
				
			||||||
                      : null),
 | 
					                      : null),
 | 
				
			||||||
              onChanged: _emailChanged,
 | 
					              onChanged: _emailChanged,
 | 
				
			||||||
@@ -72,7 +102,9 @@ class _LoginRouteState extends State<LoginRoute> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            RaisedButton(
 | 
					            RaisedButton(
 | 
				
			||||||
              child: Text(tr("Sign in")),
 | 
					              child: Text(tr("Sign in")),
 | 
				
			||||||
              onPressed: !validateEmail(_currEmail) || _currPassword.length < 3 ? null : _submitForm,
 | 
					              onPressed: !validateEmail(_currEmail) || _currPassword.length < 3
 | 
				
			||||||
 | 
					                  ? null
 | 
				
			||||||
 | 
					                  : () => _submitForm(context),
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
          ],
 | 
					          ],
 | 
				
			||||||
        ),
 | 
					        ),
 | 
				
			||||||
@@ -83,10 +115,9 @@ class _LoginRouteState extends State<LoginRoute> {
 | 
				
			|||||||
  @override
 | 
					  @override
 | 
				
			||||||
  Widget build(BuildContext context) {
 | 
					  Widget build(BuildContext context) {
 | 
				
			||||||
    return Scaffold(
 | 
					    return Scaffold(
 | 
				
			||||||
      appBar: AppBar(
 | 
					        appBar: AppBar(
 | 
				
			||||||
        title: Text("Comunic"),
 | 
					          title: Text("Comunic"),
 | 
				
			||||||
      ),
 | 
					        ),
 | 
				
			||||||
      body: _buildLoginForm()
 | 
					        body: _buildLoginForm());
 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
							
								
								
									
										41
									
								
								lib/utils/ui_utils.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								lib/utils/ui_utils.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,41 @@
 | 
				
			|||||||
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// User interface utilities
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Build and return a full loading page
 | 
				
			||||||
 | 
					Widget buildLoadingPage() {
 | 
				
			||||||
 | 
					  return Scaffold(
 | 
				
			||||||
 | 
					    body: Center(
 | 
				
			||||||
 | 
					      child: CircularProgressIndicator(),
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Build and return an error card
 | 
				
			||||||
 | 
					Widget buildErrorCard(String message) {
 | 
				
			||||||
 | 
					  return Card(
 | 
				
			||||||
 | 
					    elevation: 2.0,
 | 
				
			||||||
 | 
					    color: Colors.red,
 | 
				
			||||||
 | 
					    child: Padding(
 | 
				
			||||||
 | 
					      padding: const EdgeInsets.all(8.0),
 | 
				
			||||||
 | 
					      child: Row(
 | 
				
			||||||
 | 
					        children: <Widget>[
 | 
				
			||||||
 | 
					          Padding(
 | 
				
			||||||
 | 
					            padding: const EdgeInsets.only(right: 8.0),
 | 
				
			||||||
 | 
					            child: Icon(
 | 
				
			||||||
 | 
					              Icons.error,
 | 
				
			||||||
 | 
					              color: Colors.white,
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					          ),
 | 
				
			||||||
 | 
					          Flexible(
 | 
				
			||||||
 | 
					            child: Text(
 | 
				
			||||||
 | 
					              message,
 | 
				
			||||||
 | 
					              style: TextStyle(color: Colors.white),
 | 
				
			||||||
 | 
					              maxLines: null,
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					          )
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					      ),
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -81,6 +81,13 @@ packages:
 | 
				
			|||||||
      url: "https://pub.dartlang.org"
 | 
					      url: "https://pub.dartlang.org"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "2.0.1"
 | 
					    version: "2.0.1"
 | 
				
			||||||
 | 
					  shared_preferences:
 | 
				
			||||||
 | 
					    dependency: "direct main"
 | 
				
			||||||
 | 
					    description:
 | 
				
			||||||
 | 
					      name: shared_preferences
 | 
				
			||||||
 | 
					      url: "https://pub.dartlang.org"
 | 
				
			||||||
 | 
					    source: hosted
 | 
				
			||||||
 | 
					    version: "0.5.2"
 | 
				
			||||||
  sky_engine:
 | 
					  sky_engine:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description: flutter
 | 
					    description: flutter
 | 
				
			||||||
@@ -144,3 +151,4 @@ packages:
 | 
				
			|||||||
    version: "2.0.8"
 | 
					    version: "2.0.8"
 | 
				
			||||||
sdks:
 | 
					sdks:
 | 
				
			||||||
  dart: ">=2.1.0 <3.0.0"
 | 
					  dart: ">=2.1.0 <3.0.0"
 | 
				
			||||||
 | 
					  flutter: ">=0.1.4 <2.0.0"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -24,6 +24,9 @@ dependencies:
 | 
				
			|||||||
  # Use with the CupertinoIcons class for iOS style icons.
 | 
					  # Use with the CupertinoIcons class for iOS style icons.
 | 
				
			||||||
  cupertino_icons: ^0.1.2
 | 
					  cupertino_icons: ^0.1.2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  # Preferences are useful for a lot of things (ex: login tokens)
 | 
				
			||||||
 | 
					  shared_preferences: ^0.5.2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
dev_dependencies:
 | 
					dev_dependencies:
 | 
				
			||||||
  flutter_test:
 | 
					  flutter_test:
 | 
				
			||||||
    sdk: flutter
 | 
					    sdk: flutter
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user